package gui;

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.io.InputStream;
import java.net.URI;
import java.net.URL;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.ResourceBundle;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import javax.xml.bind.JAXBException;

import javafx.application.Platform;
import javafx.beans.property.ReadOnlyObjectWrapper;
import javafx.beans.value.ChangeListener;
import javafx.beans.value.ObservableValue;
import javafx.collections.FXCollections;
import javafx.collections.ListChangeListener;
import javafx.collections.ObservableList;
import javafx.event.Event;
import javafx.event.EventHandler;
import javafx.fxml.FXML;
import javafx.geometry.Point2D;
import javafx.scene.SnapshotParameters;
import javafx.scene.control.Button;
import javafx.scene.control.Dialogs;
import javafx.scene.control.Dialogs.DialogOptions;
import javafx.scene.control.Dialogs.DialogResponse;
import javafx.scene.control.Label;
import javafx.scene.control.ListView;
import javafx.scene.control.MenuItem;
import javafx.scene.control.SelectionMode;
import javafx.scene.control.TabPane;
import javafx.scene.control.TableCell;
import javafx.scene.control.TableColumn;
import javafx.scene.control.TableColumn.CellDataFeatures;
import javafx.scene.control.TableView;
import javafx.scene.control.TextArea;
import javafx.scene.control.TextField;
import javafx.scene.control.cell.PropertyValueFactory;
import javafx.scene.image.Image;
import javafx.scene.image.ImageView;
import javafx.scene.input.KeyCode;
import javafx.scene.input.KeyEvent;
import javafx.scene.input.MouseButton;
import javafx.scene.input.MouseEvent;
import javafx.scene.layout.Pane;
import javafx.scene.paint.Color;
import javafx.scene.shape.Rectangle;
import javafx.scene.transform.Scale;
import javafx.stage.FileChooser;
import javafx.util.Callback;
import jpsearch.JPSearchMetadataHandler;
import jpsearch.JPSearchParser;
import jpsearch.JPSearchRewriter;
import jpsearch.JPSearchTranslator;
import jpsearchcore.ContentDesciption.CDEvent;
import jpsearchcore.ContentDesciption.CDObject;
import jpsearchcore.ContentDesciption.CDOrganizationType;
import jpsearchcore.ContentDesciption.CDPerson;
import jpsearchcore.ExtDescription;
import jpsearchcore.ExternalDescription;
import jpsearchcore.GPSPositioning;
import jpsearchcore.JPSearchCore;
import jpsearchcore.OriginalImageIdentifier;
import jpsearchcore.Person;
import jpsearchcore.Place;
import jpsearchcore.Publisher;
import jpsearchcore.Rating;
import jpsearchcore.RegionLocator;
import jpsearchcore.RegionLocator.Region;
import jpsearchcore.RegionOfInterest;
import jpsearchcore.RightsDescription;
import jpsearchcore.Source;
import jpsearchcore.SourceElement;

import org.apache.commons.io.IOUtils;
import org.apache.commons.lang3.StringEscapeUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.sanselan.ImageReadException;
import org.apache.sanselan.ImageWriteException;

@SuppressWarnings({ "unchecked", "rawtypes" })
public class Controller implements javafx.fxml.Initializable {

	private Main mainApp;
	public static final String URI_REGEX = "^[(https?|ftp|file)://]?[-a-zA-Z0-9+&@#/%?=~_|!:,.;]*[-a-zA-Z0-9+&@#/%=~_|]";
	public static final String URI_REGEX2 =  "_^(?:(?:https?|ftp)://)(?:\\S+(?::\\S*)?@)?(?:(?!10(?:\\.\\d{1,3}){3})(?!127(?:\\.\\d{1,3}){3})(?!169\\.254(?:\\.\\d{1,3}){2})" +
			"(?!192\\.168(?:\\.\\d{1,3}){2})(?!172\\.(?:1[6-9]|2\\d|3[0-1])(?:\\.\\d{1,3}){2})(?:[1-9]\\d?|1\\d\\d|2[01]\\d|22[0-3])(?:\\.(?:1?\\d{1,2}|2[0-4]\\d|25[0-5])){2}" +
			"(?:\\.(?:[1-9]\\d?|1\\d\\d|2[0-4]\\d|25[0-4]))|(?:(?:[a-z\\x{00a1}-\\x{ffff}0-9]+-?)*[a-z\\x{00a1}-\\x{ffff}0-9]+)(?:\\.(?:[a-z\\x{00a1}-\\x{ffff}0-9]+-?)*[a-z\\x{00a1}" +
			"-\\x{ffff}0-9]+)*(?:\\.(?:[a-z\\x{00a1}-\\x{ffff}]{2,})))(?::\\d{2,5})?(?:/[^\\s]*)?$_iuS";
	public static final int STROKE_SIZE = 2;
	
	@FXML private TabPane tabpaneAdvanced;

	//Menu Items
	@FXML private MenuItem menuItemUpdate;
	@FXML private MenuItem menuItemSaveXMLToFile;
	@FXML private MenuItem menuItemOpenXMLFile;
	@FXML private MenuItem menuItemSaveToDB;
	@FXML private MenuItem menuShowXML;

	//TextFields
	@FXML private TextField txtIdentifier;
	@FXML private TextField txtWidth;
	@FXML private TextField txtHeight;
	@FXML private TextArea txtDescription;
	@FXML private TextField txtTitle;
	@FXML private TextField txtPreferredValue;
	@FXML private TextField txtCreationDate;
	@FXML private TextField txtModifiedDate;

	@FXML private TextField txtAddKeyword;
	@FXML private ListView<String> lstKeywords;

	@FXML private TextField txtAddCollectionLabel;
	@FXML private ListView<String> lstCollectionLabel;

	//Publisher
	@FXML private Button btnAddPublisher;
	@FXML private TextField txtPubGName;
	@FXML private TextField txtPubFName;
	@FXML private TableView<Publisher> tblPublishers;	
	TableColumn pubGivenNameCol = new TableColumn("Given Name");
	TableColumn pubFamilyNameCol = new TableColumn("Family Name");
	TableColumn pubOrgName = new TableColumn("Organization Name");
	TableColumn pubOrgAddressName = new TableColumn("Organization Address Name");
	TableColumn pubOrgAddressDescription = new TableColumn("Organization Address Description");

	//GPS Positioning
	@FXML private TextField txtGPSlongitude;
	@FXML private TextField txtGPSlatitude;
	@FXML private TextField txtGPSaltitude;

	//Source
	@FXML private TableView<Source> tblSources;
	@FXML private TableView<SourceElement> tblSourceElements;
	@FXML private Button btnSourcesDelete;
	@FXML private Button btnSourcesAdd;
	@FXML private Button btnSourceElementsDelete;
	@FXML private Button btnSourceElementsAdd;
	private TableColumn sourceElemType = new TableColumn("Source Element Type");
	private TableColumn creationMethod = new TableColumn("Creation Method");
	private TableColumn creationDescription = new TableColumn("Creation Description");
	private TableColumn sourceElementTitle = new TableColumn("Source Element Title");
	private TableColumn sourceElementDescription = new TableColumn("Source Element Description");
	private TableColumn sourceElementIdentifier = new TableColumn("Source Element Identifier");

	//Rating
	@FXML private TextField txtRatingLblDefinition;
	@FXML private TextField txtRatingLblValue;

	//Original Image Identifier
	@FXML private TextField txtOIIOriginationofID;
	@FXML private TextField txtOIIIdentifer;

	//Rights Description
	@FXML private TextField txtRDrdi;
	@FXML private TextField txtRDdescription;
	@FXML private TextField txtRDardr;
	@FXML private TextField txtRDard;	

	//TextAreas
	@FXML private TextArea txaPubOrgName;
	@FXML private TextArea txaPubOrgAddName;
	@FXML private TextArea txaPubOrgAddDes;

	//Buttons
	@FXML private Button btnCreatorsDelete;
	@FXML private Button btnModifiersDelete;
	@FXML private Button btnCreatorsAdd;
	@FXML private Button btnModifiersAdd;

	//Labels
	@FXML private Label lblFileName;

	//ImageView
	@FXML private ImageView imgThumbnail;

	//Creators and Modifiers
	@FXML private TableView<Person> tblCreators;
	@FXML private TableView<Person> tblModifiers;
	private TableColumn crGivenName = new TableColumn("Given Name");
	private TableColumn crFamilyName = new TableColumn("Family Name");
	private TableColumn modGivenName = new TableColumn("Given Name");
	private TableColumn modFamilyName = new TableColumn("Family Name");

	//Region of Interest
	@FXML private TableView<RegionOfInterest> tblRegionOfInterest;
	@FXML private TableView<ExtDescription> tblExternalDescription;
	@FXML private TableView<CDPerson> tblROIPerson;
	@FXML private TableView<CDOrganizationType> tblROIPersonAffiliation;
	@FXML private TableView<CDObject> tblROIObject;
	@FXML private TableView<Place> tblROIPlace;
	@FXML private TableView<CDEvent> tblROIEvent;

	private TableColumn roiTitle = new TableColumn("Title");
	private TableColumn roiDescription = new TableColumn("Description");

	private TableColumn tnFromNameSpace = new TableColumn("tnFromNameSpace");
	private TableColumn tnFromNameSpacePrefix = new TableColumn("tnFromNameSpacePrefix");
	private TableColumn lvFromControlledVocabularyOfLiterals = new TableColumn("lvFromControlledVocabularyOfLiterals");
	private TableColumn lvFromGrammarOfLiterals = new TableColumn("lvFromGrammarOfLiterals");
	private TableColumn rvFromNameSpace = new TableColumn("rvFromNameSpace");
	private TableColumn svFromNameSpace = new TableColumn("svFromNameSpace");

	private TableColumn roiPersonGivenName = new TableColumn("Given Name");
	private TableColumn roiPersonFamilyName = new TableColumn("Family Name");
	private TableColumn roiPersonDescription = new TableColumn("Description");
	private TableColumn roiPersonNationality = new TableColumn("Nationality");
	private TableColumn roiPersonAddressName = new TableColumn("Address Name");
	private TableColumn roiPersonAddressDescription = new TableColumn("Address Description");

	private TableColumn roiPersonAffNames = new TableColumn("Names");

	private TableColumn roiObjectName = new TableColumn("Name");
	private TableColumn roiObjectDesc = new TableColumn("Description");
	private TableColumn roiObjectLabel = new TableColumn("Label");

	private TableColumn roiPlaceName = new TableColumn("Place Name");
	private TableColumn roiPlaceDesc = new TableColumn("Place Description");

	private TableColumn roiEventLabel = new TableColumn("Event Label");
	private TableColumn roiEventDesc = new TableColumn("Event Description");	

	@FXML private TextArea txaTNContent;
	@FXML private TextArea txaLVContent;
	@FXML private TextArea txaRVContent;
	@FXML private TextArea txaSVContent;

	@FXML private TextArea txaPersonAffiliationAddressName;
	@FXML private TextArea txaPersonAffiliationAddressDesc;

	@FXML private Button btnROIAdd;
	@FXML private Button btnROIDelete;
	@FXML private Button btnEDDelete;
	@FXML private Button btnEDAdd;
	@FXML private Button btnPersonAffDelete;
	@FXML private Button btnPersonAffAdd;
	@FXML private Button btnPersonDelete;
	@FXML private Button btnPersonAdd;
	@FXML private Button btnObjectAdd;
	@FXML private Button btnObjectDelete;
	@FXML private Button btnPlaceDel;
	@FXML private Button btnPlaceAdd;
	@FXML private Button btnEventDel;
	@FXML private Button btnEventAdd;
	@FXML private Button btnAddStructuredValue;

	@FXML private ListView<String> lstRegionLocator;
	@FXML private ListView<String> lstROIKeywords;
	@FXML private TextField txtAddKeywordROI;
	@FXML private ImageView imgScreenshot;

	// ------------------------------------------------------------------------------------------------------------------------	
	// Class objects
	private JPSearchCore jpsC;
	private JPSearchParser image;
	private Image actualImage;
	private File inputImageFile;
	private String currentPath = System.getProperty("user.dir");
	private File dbFile = new File(currentPath + "/metadata.db");
	private boolean saved = true;
	String currentXML = "";

	/**
	 * Is called by the main application to give a reference back to itself.
	 * 
	 * @param mainApp
	 */
	public void setMainApp(Main mainApp) {
		this.mainApp = mainApp;
	}

	@Override	
	public void initialize(URL location, ResourceBundle resources) {
		setDisableInterface(true);
		clearAllFields();
		initializeAdditionalInfo();
		initializeCreatorsModifiers();
		initializePublishers();
		initializeSources();
		initializeRegionOfInterest();
	}

	/**
	 * Initializes Additional Information Section
	 */
	public void initializeAdditionalInfo(){

		/* 
		 * TYPE CHECKING
		 */
		txtIdentifier.focusedProperty().addListener(new ChangeListener<Boolean>() {
			@Override
			public void changed(ObservableValue<? extends Boolean> arg0, Boolean arg1, Boolean loseFocus) {
				// Handling only when focus is out.
				if(!loseFocus && StringUtils.isBlank(txtIdentifier.getText()) == false){
					Pattern p = Pattern.compile(URI_REGEX);
					Matcher m = p.matcher(txtIdentifier.getText());

					if(m.find() == false){
						Dialogs.showErrorDialog(null, "Identifier is of an invalid type. [anyURI]", "Error", "JPSearch Editor");

						txtIdentifier.selectAll();
						Platform.runLater(new Runnable() {
							@Override
							public void run() {
								txtIdentifier.requestFocus();
							}
						});
					}
					else{
						saved = false;
					}
				}
			}
		});

		txtPreferredValue.focusedProperty().addListener(new ChangeListener<Boolean>() {
			@Override
			public void changed(ObservableValue<? extends Boolean> arg0, Boolean arg1, Boolean loseFocus) {
				// Handling only when focus is out.
				if(!loseFocus){					
					if (StringUtils.isNumeric(txtPreferredValue.getText()) == false) {
						Dialogs.showErrorDialog(null, "Preferred value is not an integer format.", "Error", "JPSearch Editor");
						txtPreferredValue.selectAll();
						Platform.runLater(new Runnable() {
							@Override
							public void run() {
								txtPreferredValue.requestFocus();
							}
						});
					}
					else{
						saved = false;
					}
				}
			}
		});

		//Add keywords by pressing enter
		txtAddKeyword.setOnKeyPressed(new EventHandler<KeyEvent>() {
			public void handle(KeyEvent ke) {		        
				if(ke.getCode() == KeyCode.ENTER){
					handleAddToKeywords();
				}
			}
		});

		//Add collection labels by pressing enter
		txtAddCollectionLabel.setOnKeyPressed(new EventHandler<KeyEvent>() {
			public void handle(KeyEvent ke) {		        
				if(ke.getCode() == KeyCode.ENTER){
					handleAddToCollectionLabel();
				}
			}
		});

		txtRDdescription.focusedProperty().addListener(new ChangeListener<Boolean>() {
			@Override
			public void changed(ObservableValue<? extends Boolean> arg0, Boolean arg1, Boolean loseFocus) {
				// Handling only when focus is out.
				if(!loseFocus && StringUtils.isBlank(txtRDdescription.getText()) == false){					
					Pattern p = Pattern.compile(URI_REGEX);
					Matcher m = p.matcher(txtRDdescription.getText());

					if(m.find() == false){
						Dialogs.showErrorDialog(null, "Rights Description Information is of an invalid type. [anyURI]", "Error", "JPSearch Editor");

						txtRDdescription.selectAll();
						Platform.runLater(new Runnable() {
							@Override
							public void run() {
								txtRDdescription.requestFocus();
							}
						});
					}
					else{
						saved = false;
					}
				}
			}
		});

		txtRDard.focusedProperty().addListener(new ChangeListener<Boolean>() {
			@Override
			public void changed(ObservableValue<? extends Boolean> arg0, Boolean arg1, Boolean loseFocus) {
				// Handling only when focus is out.
				if(!loseFocus && StringUtils.isBlank(txtRDard.getText()) == false){					
					Pattern p = Pattern.compile(URI_REGEX);
					Matcher m = p.matcher(txtRDard.getText());

					if(m.find() == false){
						Dialogs.showErrorDialog(null, "Actual Rights Description Reference is of an invalid type. [anyURI]", "Error", "JPSearch Editor");

						txtRDard.selectAll();
						Platform.runLater(new Runnable() {
							@Override
							public void run() {
								txtRDard.requestFocus();
							}
						});
					}
					else{
						saved = false;
					}
				}
			}
		});

		txtRatingLblDefinition.focusedProperty().addListener(new ChangeListener<Boolean>() {
			@Override
			public void changed(ObservableValue<? extends Boolean> arg0, Boolean arg1, Boolean loseFocus) {
				// Handling only when focus is out.
				if(!loseFocus && StringUtils.isBlank(txtRatingLblDefinition.getText()) == false){					
					Pattern p = Pattern.compile(URI_REGEX);
					Matcher m = p.matcher(txtRatingLblDefinition.getText());

					if(m.find() == false){
						Dialogs.showErrorDialog(null, "Label Definition Reference is of an invalid type. [anyURI]", "Error", "JPSearch Editor");

						txtRatingLblDefinition.selectAll();
						Platform.runLater(new Runnable() {
							@Override
							public void run() {
								txtRatingLblDefinition.requestFocus();
							}
						});
					}
					else{
						saved = false;
					}
				}
			}
		});

		txtOIIOriginationofID.focusedProperty().addListener(new ChangeListener<Boolean>() {
			@Override
			public void changed(ObservableValue<? extends Boolean> arg0, Boolean arg1, Boolean loseFocus) {
				// Handling only when focus is out.
				if(!loseFocus && StringUtils.isBlank(txtOIIOriginationofID.getText()) == false){					
					Pattern p = Pattern.compile(URI_REGEX);
					Matcher m = p.matcher(txtOIIOriginationofID.getText());

					if(m.find() == false){
						Dialogs.showErrorDialog(null, "Origination Of ID is of an invalid type. [anyURI]", "Error", "JPSearch Editor");

						txtOIIOriginationofID.selectAll();
						Platform.runLater(new Runnable() {
							@Override
							public void run() {
								txtOIIOriginationofID.requestFocus();
							}
						});
					}
					else{
						saved = false;
					}
				}
			}
		});

		txtWidth.focusedProperty().addListener(new ChangeListener<Boolean>() {
			@Override
			public void changed(ObservableValue<? extends Boolean> arg0, Boolean arg1, Boolean loseFocus) {
				// Handling only when focus is out.
				if(!loseFocus){					
					if (StringUtils.isNumeric(txtWidth.getText()) == false) {
						Dialogs.showErrorDialog(null, "Width is not an integer format.", "Error", "JPSearch Editor");
						txtWidth.selectAll();
						Platform.runLater(new Runnable() {
							@Override
							public void run() {
								txtWidth.requestFocus();
							}
						});
					}
					else{
						saved = false;
					}
				}
			}
		});

		txtHeight.focusedProperty().addListener(new ChangeListener<Boolean>() {
			@Override
			public void changed(ObservableValue<? extends Boolean> arg0, Boolean arg1, Boolean loseFocus) {
				// Handling only when focus is out.
				if(!loseFocus){					
					if (StringUtils.isNumeric(txtHeight.getText()) == false) {
						Dialogs.showErrorDialog(null, "Height is not an integer format.", "Error", "JPSearch Editor");
						txtHeight.selectAll();
						Platform.runLater(new Runnable() {
							@Override
							public void run() {
								txtHeight.requestFocus();
							}
						});
					}
					else{
						saved = false;
					}
				}
			}
		});
		//------------------------------------------------------------------

		/*
		 * GPS Number range/type checking 
		 */
		txtGPSlongitude.focusedProperty().addListener(new ChangeListener<Boolean>() {
			@Override
			public void changed(ObservableValue<? extends Boolean> arg0, Boolean arg1, Boolean loseFocus) {
				// Handling only when focus is out.
				if(!loseFocus){	
					if(StringUtils.isNumeric(txtGPSlongitude.getText()) == false){
						Dialogs.showErrorDialog(null, "Longitude format is not integer.", "Error", "JPSearch Editor");
						txtGPSlongitude.setText("0.0");
						txtGPSlongitude.selectAll();
						Platform.runLater(new Runnable() {
							@Override
							public void run() {
								txtGPSlongitude.requestFocus();
							}
						});
						return;
					}

					double longitude = Double.parseDouble(txtGPSlongitude.getText());
					if(longitude < -180.0 || longitude > 180.0){
						Dialogs.showErrorDialog(null, "Longitude \"" + longitude + "\" is out of range [-180, 180]");
						txtGPSlongitude.setText("0.0");
						txtGPSlongitude.selectAll();
						Platform.runLater(new Runnable() {
							@Override
							public void run() {
								txtGPSlongitude.requestFocus();
							}
						});
					}
					else{
						saved = false;
					}
				}
			}
		});
		txtGPSlatitude.focusedProperty().addListener(new ChangeListener<Boolean>() {
			@Override
			public void changed(ObservableValue<? extends Boolean> arg0, Boolean arg1, Boolean loseFocus) {
				// Handling only when focus is out.
				if(!loseFocus){		
					if(StringUtils.isNumeric(txtGPSlatitude.getText()) == false){
						Dialogs.showErrorDialog(null, "Latitude format is not integer.", "Error", "JPSearch Editor");
						txtGPSlatitude.setText("0.0");
						txtGPSlatitude.selectAll();
						Platform.runLater(new Runnable() {
							@Override
							public void run() {
								txtGPSlatitude.requestFocus();
							}
						});
						return;
					}

					double latitude = Double.parseDouble(txtGPSlatitude.getText());
					if(latitude < -90.0 || latitude > 90.0){
						Dialogs.showErrorDialog(null, "Latitude \"" + latitude + "\" is out of range [-90, 90]");
						txtGPSlatitude.setText("0.0");
						txtGPSlatitude.selectAll();
						Platform.runLater(new Runnable() {
							@Override
							public void run() {
								txtGPSlatitude.requestFocus();
							}
						});					
					}
					else{
						saved = false;
					}
				}
			}
		});

		txtGPSaltitude.focusedProperty().addListener(new ChangeListener<Boolean>() {
			@Override
			public void changed(ObservableValue<? extends Boolean> arg0, Boolean arg1, Boolean loseFocus) {
				// Handling only when focus is out.
				if(!loseFocus){		
					if(StringUtils.isNumeric(txtGPSaltitude.getText()) == false){
						Dialogs.showErrorDialog(null, "Altitude format is not integer.", "Error", "JPSearch Editor");
						txtGPSaltitude.setText("0.0");
						txtGPSaltitude.selectAll();
						Platform.runLater(new Runnable() {
							@Override
							public void run() {
								txtGPSaltitude.requestFocus();
							}
						});
					}
					else{
						saved = false;
					}
				}
			}
		});
		//----------------------------------------------------

		//Keywords
		lstKeywords.setOnKeyPressed(new EventHandler<KeyEvent>() {
			public void handle(KeyEvent ke) {		        
				if(ke.getCode() == KeyCode.DELETE){
					if(Dialogs.showConfirmDialog(null, "Delete this keyword?","Confirmation","JPSearch Editor", DialogOptions.YES_NO) == DialogResponse.YES){
						int selected = lstKeywords.getSelectionModel().getSelectedIndex();
						lstKeywords.getItems().remove(selected);
						jpsC.getKeywords().remove(selected);
						lstKeywords.getSelectionModel().clearSelection();
						saved = false;
					}
				}
			}
		});

		//Collection Label
		lstCollectionLabel.setOnKeyPressed(new EventHandler<KeyEvent>() {
			public void handle(KeyEvent ke) {		        
				if(ke.getCode() == KeyCode.DELETE){
					if(Dialogs.showConfirmDialog(null, "Delete this keyword?","Confirmation","JPSearch Editor", DialogOptions.YES_NO) == DialogResponse.YES){
						int selected = lstCollectionLabel.getSelectionModel().getSelectedIndex();
						lstCollectionLabel.getItems().remove(selected);
						jpsC.getCollectionLabel().remove(selected);
						lstCollectionLabel.getSelectionModel().clearSelection();
						saved = false;
					}
				}
			}
		});
	}

	/**
	 * Initializes Creators and Modifiers part 
	 */
	public void initializeCreatorsModifiers(){

		crGivenName.setCellValueFactory(new Callback<CellDataFeatures<Person, String>, ObservableValue<String>>() {
			public ObservableValue<String> call(CellDataFeatures<Person, String> p) {	
				if(p.getValue().getGivenName() == null)
					return new ReadOnlyObjectWrapper("");
				return new ReadOnlyObjectWrapper(p.getValue().getGivenName());
			}
		});
		crGivenName.prefWidthProperty().bind(tblPublishers.widthProperty().divide(4));
		crGivenName.setEditable(true);
		
		crFamilyName.setCellValueFactory(new Callback<CellDataFeatures<Person, String>, ObservableValue<String>>() {
			public ObservableValue<String> call(CellDataFeatures<Person, String> p) {	
				if(p.getValue().getFamilyName() == null)
					return new ReadOnlyObjectWrapper("");
				return new ReadOnlyObjectWrapper(p.getValue().getFamilyName());
			}
		});
		crFamilyName.prefWidthProperty().bind(tblPublishers.widthProperty().divide(4));
		crFamilyName.setEditable(true);
		
		modGivenName.setCellValueFactory(new Callback<CellDataFeatures<Person, String>, ObservableValue<String>>() {
			public ObservableValue<String> call(CellDataFeatures<Person, String> p) {	
				if(p.getValue().getGivenName() == null)
					return new ReadOnlyObjectWrapper("");
				return new ReadOnlyObjectWrapper(p.getValue().getGivenName());
			}
		});
		modGivenName.prefWidthProperty().bind(tblPublishers.widthProperty().divide(4));
		modGivenName.setEditable(true);
				
		modFamilyName.setCellValueFactory(new Callback<CellDataFeatures<Person, String>, ObservableValue<String>>() {
			public ObservableValue<String> call(CellDataFeatures<Person, String> p) {	
				if(p.getValue().getFamilyName() == null)
					return new ReadOnlyObjectWrapper("");
				return new ReadOnlyObjectWrapper(p.getValue().getFamilyName());
			}
		});
		modFamilyName.prefWidthProperty().bind(tblPublishers.widthProperty().divide(4));
		modFamilyName.setEditable(true);
		
		Callback<TableColumn, TableCell> cellFactoryPerson = new Callback<TableColumn, TableCell>() {
			public TableCell call(TableColumn p) {
				return new EditingCell<Person>();
			}
		};

		//initialize TableView - Bindings
		tblCreators.getColumns().clear();
		tblCreators.getColumns().addAll(crGivenName, crFamilyName);
		tblCreators.getSelectionModel().setSelectionMode(SelectionMode.SINGLE);
		tblModifiers.getColumns().clear();
		tblModifiers.getColumns().addAll(modGivenName, modFamilyName);
		tblModifiers.getSelectionModel().setSelectionMode(SelectionMode.SINGLE);

		crGivenName.setCellFactory(cellFactoryPerson);
		crFamilyName.setCellFactory(cellFactoryPerson);
		modGivenName.setCellFactory(cellFactoryPerson);
		modFamilyName.setCellFactory(cellFactoryPerson);

		//Cell editing handler for Creators and Modifiers
		crGivenName.setOnEditCommit(
				new EventHandler<TableColumn.CellEditEvent<Person, String>>() {
					@Override
					public void handle(TableColumn.CellEditEvent<Person, String> t) {
						(t.getTableView().getItems().get(t.getTablePosition().getRow())).setGivenName(t.getNewValue());
						saved = false;
					}
				});	
		
		crFamilyName.setOnEditCommit( 
				new EventHandler<TableColumn.CellEditEvent<Person, String>>() {
					@Override
					public void handle(TableColumn.CellEditEvent<Person, String> t) {
						(t.getTableView().getItems().get(t.getTablePosition().getRow())).setFamilyName(t.getNewValue());
						saved = false;
					}
				});		
		
		modGivenName.setOnEditCommit(
				new EventHandler<TableColumn.CellEditEvent<Person, String>>() {
					@Override
					public void handle(TableColumn.CellEditEvent<Person, String> t) {
						(t.getTableView().getItems().get(t.getTablePosition().getRow())).setGivenName(t.getNewValue());
						saved = false;
					}
				});
		
		modFamilyName.setOnEditCommit(
				new EventHandler<TableColumn.CellEditEvent<Person, String>>() {
					@Override
					public void handle(TableColumn.CellEditEvent<Person, String> t) {
						(t.getTableView().getItems().get(t.getTablePosition().getRow())).setFamilyName(t.getNewValue());
						saved = false;
					}
				});
		
		btnCreatorsAdd.setOnAction(new EventHandler() {			 
			@Override
			public void handle(Event arg0) { 
				if(jpsC != null){
					tblCreators.getItems().add(new Person("[GivenName]","[FamilyName]"));
					tblCreators.getSelectionModel().selectLast();
					saved = false;
				}
			}
		});

		btnModifiersAdd.setOnAction(new EventHandler() {			 
			@Override
			public void handle(Event arg0) { 
				if(jpsC != null){
					tblModifiers.getItems().add(new Person("[GivenName]","[FamilyName]"));
					tblModifiers.getSelectionModel().selectLast();  
					saved = false;
				}
			}
		});

		btnCreatorsDelete.setOnAction(new EventHandler() {			 
			@Override
			public void handle(Event arg0) { 
				if(jpsC != null){
					int index = tblCreators.getSelectionModel().getSelectedIndex();
					if(index >= 0){
						if(Dialogs.showConfirmDialog(null, "Are you sure you want to delete this Creator?", "Confirmation", "JPSearch Editor", DialogOptions.YES_NO) == DialogResponse.YES){
							tblCreators.getItems().remove(index);
							tblCreators.getSelectionModel().selectNext(); 
							saved = false;
						}
					}
					else{
						Dialogs.showWarningDialog(null, "No items selected" , "Warning", "JPSearch Editor");
					}
				}
			}
		});

		btnModifiersDelete.setOnAction(new EventHandler() {			 
			@Override
			public void handle(Event arg0) { 
				if(jpsC != null){
					int index = tblModifiers.getSelectionModel().getSelectedIndex();
					if(index >= 0){
						if(Dialogs.showConfirmDialog(null, "Are you sure you want to delete this Modifier?", "Confirmation", "JPSearch Editor", DialogOptions.YES_NO) == DialogResponse.YES){
							tblModifiers.getItems().remove(index);
							tblModifiers.getSelectionModel().clearSelection(); 
							saved = false;
						}
					}
					else{
						Dialogs.showWarningDialog(null, "No items exist selected" , "Warning", "JPSearch Editor");
					}
				}
			}
		});
	}

	/**
	 * Initializes Publishers 
	 */
	public void initializePublishers(){

		pubGivenNameCol.setCellValueFactory(new Callback<CellDataFeatures<Publisher, String>, ObservableValue<String>>() {
			public ObservableValue<String> call(CellDataFeatures<Publisher, String> p) {	
				if(p.getValue().getPersonGivenName() == null)
					return new ReadOnlyObjectWrapper("");
				return new ReadOnlyObjectWrapper(p.getValue().getPersonGivenName());
			}
		});

		pubGivenNameCol.prefWidthProperty().bind(tblPublishers.widthProperty().divide(6));
		pubGivenNameCol.setEditable(true);
		//---
		pubFamilyNameCol.setCellValueFactory(new Callback<CellDataFeatures<Publisher, String>, ObservableValue<String>>() {
			public ObservableValue<String> call(CellDataFeatures<Publisher, String> p) {	
				if(p.getValue().getPersonFamilyName() == null)
					return new ReadOnlyObjectWrapper("");
				return new ReadOnlyObjectWrapper(p.getValue().getPersonFamilyName());
			}
		});
		pubFamilyNameCol.prefWidthProperty().bind(tblPublishers.widthProperty().divide(6));
		pubFamilyNameCol.setEditable(true);
		//---
		pubOrgName.setCellValueFactory(new Callback<CellDataFeatures<Publisher, String>, ObservableValue<String>>() {
			public ObservableValue<String> call(CellDataFeatures<Publisher, String> p) {	
				if(p.getValue().getOrgInfoName() == null)
					return new ReadOnlyObjectWrapper("");
				return new ReadOnlyObjectWrapper(p.getValue().getOrgInfoName());
			}
		});
		pubOrgName.prefWidthProperty().bind(tblPublishers.widthProperty().divide(5));
		pubOrgName.setEditable(true);
		//---	
		pubOrgAddressName.setCellValueFactory(new Callback<CellDataFeatures<Publisher, String>, ObservableValue<String>>() {
			public ObservableValue<String> call(CellDataFeatures<Publisher, String> p) {	
				if(p.getValue().getOrgInfoAddressName() == null)
					return new ReadOnlyObjectWrapper("");
				return new ReadOnlyObjectWrapper(p.getValue().getOrgInfoAddressName());
			}
		});
		pubOrgAddressName.prefWidthProperty().bind(tblPublishers.widthProperty().divide(4));
		pubOrgAddressName.setEditable(true);
		//------
		pubOrgAddressDescription.setCellValueFactory(new Callback<CellDataFeatures<Publisher, String>, ObservableValue<String>>() {
			public ObservableValue<String> call(CellDataFeatures<Publisher, String> p) {
				if(p.getValue().getOrgInfoAddressDescr() == null)
					return new ReadOnlyObjectWrapper("");
				return new ReadOnlyObjectWrapper(p.getValue().getOrgInfoAddressDescr());
			}
		});
		pubOrgAddressDescription.prefWidthProperty().bind(tblPublishers.widthProperty().divide(4));
		pubOrgAddressDescription.setEditable(true);
		//------

		tblPublishers.getColumns().clear();
		tblPublishers.getColumns().addAll(pubGivenNameCol, pubFamilyNameCol, pubOrgName, pubOrgAddressName, pubOrgAddressDescription);		
		tblPublishers.getSelectionModel().setSelectionMode(SelectionMode.SINGLE);
		//----------------------------------------------

		Callback<TableColumn, TableCell> cellFactoryPerson = new Callback<TableColumn, TableCell>() {
			public TableCell call(TableColumn p) {
				return new EditingCell<Person>();
			}
		};

		Callback<TableColumn, TableCell> cellFactoryPublisher = new Callback<TableColumn, TableCell>() {
			public TableCell call(TableColumn p) {
				return new EditingCell<Publisher>();
			}
		};

		pubGivenNameCol.setCellFactory(cellFactoryPerson);
		pubFamilyNameCol.setCellFactory(cellFactoryPerson);	
		pubOrgName.setCellFactory(cellFactoryPublisher);
		pubOrgAddressName.setCellFactory(cellFactoryPublisher);
		pubOrgAddressDescription.setCellFactory(cellFactoryPublisher);

		pubGivenNameCol.setOnEditCommit(
				new EventHandler<TableColumn.CellEditEvent<Publisher, String>>() {
					@Override
					public void handle(TableColumn.CellEditEvent<Publisher, String> t) {
						(t.getTableView().getItems().get(t.getTablePosition().getRow())).setPersonGivenName(t.getNewValue());
						saved = false;
					}
				});
		pubFamilyNameCol.setOnEditCommit(
				new EventHandler<TableColumn.CellEditEvent<Publisher, String>>() {
					@Override
					public void handle(TableColumn.CellEditEvent<Publisher, String> t) {
						(t.getTableView().getItems().get(t.getTablePosition().getRow())).setPersonFamilyName(t.getNewValue());
						saved = false;
					}
				});
		pubOrgName.setOnEditCommit(
				new EventHandler<TableColumn.CellEditEvent<Publisher, String>>() {
					@Override
					public void handle(TableColumn.CellEditEvent<Publisher, String> t) {
						(t.getTableView().getItems().get(t.getTablePosition().getRow())).setOrgInfoName(t.getNewValue());
						saved = false;
					}
				});
		pubOrgAddressName.setOnEditCommit(
				new EventHandler<TableColumn.CellEditEvent<Publisher, String>>() {
					@Override
					public void handle(TableColumn.CellEditEvent<Publisher, String> t) {
						(t.getTableView().getItems().get(t.getTablePosition().getRow())).setOrgInfoAddressName(t.getNewValue());
						saved = false;
					}
				});
		pubOrgAddressDescription.setOnEditCommit(
				new EventHandler<TableColumn.CellEditEvent<Publisher, String>>() {
					@Override
					public void handle(TableColumn.CellEditEvent<Publisher, String> t) {
						(t.getTableView().getItems().get(t.getTablePosition().getRow())).setOrgInfoAddressDescription(t.getNewValue());
						saved = false;
					}
				});

		//On click table publishers
		tblPublishers.getSelectionModel().selectedItemProperty().addListener(new ChangeListener() {

			@Override
			public void changed(ObservableValue arg0, Object arg1, Object arg2) {
				if(arg0.getValue() != null){
					Publisher pub = (Publisher)arg0.getValue();
					txtPubGName.setText(pub.getPersonGivenName());
					txtPubFName.setText(pub.getPersonFamilyName());
					txaPubOrgName.setText(pub.getOrgInfoName());
					txaPubOrgAddName.setText(pub.getOrgInfoAddressName());
					txaPubOrgAddDes.setText(pub.getOrgInfoAddressDescr());
					btnAddPublisher.setText("Update Publisher");
				}
			}
		});

	}

	/**
	 * Initialize Sources and Source Element 
	 */
	public void initializeSources(){

		tblSources.getColumns().clear();
		tblSources.getColumns().addAll(sourceElemType, creationMethod, creationDescription);
		tblSources.getSelectionModel().setSelectionMode(SelectionMode.SINGLE);		
		sourceElemType.setCellValueFactory(new PropertyValueFactory<Source,String>("SourceElementType"));
		sourceElemType.prefWidthProperty().bind(tblSources.widthProperty().divide(3));
		sourceElemType.setEditable(true);
		creationMethod.setCellValueFactory(new PropertyValueFactory<Source,String>("CreationMethod"));
		creationMethod.prefWidthProperty().bind(tblSources.widthProperty().divide(3));
		creationMethod.setEditable(true);
		creationDescription.setCellValueFactory(new PropertyValueFactory<Source,String>("CreationDescription"));
		creationDescription.prefWidthProperty().bind(tblSources.widthProperty().divide(3));
		creationDescription.setEditable(true);

		tblSourceElements.getColumns().clear();
		tblSourceElements.getColumns().addAll(sourceElementTitle, sourceElementDescription, sourceElementIdentifier);
		tblSourceElements.getSelectionModel().setSelectionMode(SelectionMode.SINGLE);
		sourceElementTitle.setCellValueFactory(new PropertyValueFactory<SourceElement,String>("SourceElementTitle"));
		sourceElementTitle.prefWidthProperty().bind(tblSourceElements.widthProperty().divide(3));
		sourceElementTitle.setEditable(true);
		sourceElementDescription.setCellValueFactory(new PropertyValueFactory<SourceElement,String>("SourceElementDescription"));
		sourceElementDescription.prefWidthProperty().bind(tblSourceElements.widthProperty().divide(3));
		sourceElementDescription.setEditable(true);
		sourceElementIdentifier.setCellValueFactory(new PropertyValueFactory<SourceElement,String>("SourceElementIdentifier"));
		sourceElementIdentifier.prefWidthProperty().bind(tblSourceElements.widthProperty().divide(3));
		sourceElementIdentifier.setEditable(true);

		Callback<TableColumn, TableCell> cellFactorySource = new Callback<TableColumn, TableCell>() {
			public TableCell call(TableColumn p) {
				return new EditingCell<Source>();
			}
		};

		Callback<TableColumn, TableCell> cellFactorySourceElement = new Callback<TableColumn, TableCell>() {
			public TableCell call(TableColumn p) {
				return new EditingCell<SourceElement>();
			}
		};

		Callback<TableColumn, TableCell> anyURIFactory = new Callback<TableColumn, TableCell>() {
			@Override
			public TableCell call(TableColumn p) {
				return new AnyUriEditableTableCell();
			}
		};


		sourceElemType.setCellFactory(cellFactorySource);
		creationMethod.setCellFactory(cellFactorySource);
		creationDescription.setCellFactory(cellFactorySource);

		sourceElementTitle.setCellFactory(cellFactorySourceElement);
		sourceElementDescription.setCellFactory(cellFactorySourceElement);
		sourceElementIdentifier.setCellFactory(anyURIFactory);


		//Cell editing for Source
		sourceElemType.setOnEditCommit(
				new EventHandler<TableColumn.CellEditEvent<Source, String>>() {
					@Override
					public void handle(TableColumn.CellEditEvent<Source, String> t) {
						(t.getTableView().getItems().get(t.getTablePosition().getRow())).setSourceElementType(t.getNewValue());
						saved = false;
					}
				});
		creationMethod.setOnEditCommit(
				new EventHandler<TableColumn.CellEditEvent<Source, String>>() {
					@Override
					public void handle(TableColumn.CellEditEvent<Source, String> t) {
						(t.getTableView().getItems().get(t.getTablePosition().getRow())).setCreationMethod(t.getNewValue());
						saved = false;
					}
				});
		creationDescription.setOnEditCommit(
				new EventHandler<TableColumn.CellEditEvent<Source, String>>() {
					@Override
					public void handle(TableColumn.CellEditEvent<Source, String> t) {
						int rowSelected = t.getTablePosition().getRow();
						(t.getTableView().getItems().get(rowSelected)).setCreationDescription(t.getNewValue());
						saved = false;
					}
				});

		//Cell editing for SourceElements table
		sourceElementTitle.setOnEditCommit(
				new EventHandler<TableColumn.CellEditEvent<SourceElement, String>>() {
					@Override
					public void handle(TableColumn.CellEditEvent<SourceElement, String> t) {
						int rowSelected = t.getTablePosition().getRow();
						(t.getTableView().getItems().get(rowSelected)).setSourceElementTitle(t.getNewValue());
						saved = false;
					}
				});
		sourceElementDescription.setOnEditCommit(
				new EventHandler<TableColumn.CellEditEvent<SourceElement, String>>() {
					@Override
					public void handle(TableColumn.CellEditEvent<SourceElement, String> t) {
						int rowSelected = t.getTablePosition().getRow();
						(t.getTableView().getItems().get(rowSelected)).setSourceElementDescription(t.getNewValue());
						saved = false;
					}
				});
		sourceElementIdentifier.setOnEditCommit(
				new EventHandler<TableColumn.CellEditEvent<SourceElement, String>>() {
					@Override
					public void handle(TableColumn.CellEditEvent<SourceElement, String> t) {
						int rowSelected = t.getTablePosition().getRow();
						(t.getTableView().getItems().get(rowSelected)).setSourceElementIdentifier(t.getNewValue());
						saved = false;
					}
				});

		//Row clicking in Sources table event
		tblSources.getSelectionModel().selectedItemProperty().addListener( new ChangeListener() {

			@Override
			public void changed(ObservableValue arg0, Object arg1, Object arg2) {
				if(arg0.getValue() != null){
					ObservableList<SourceElement> sourceElements = 
							FXCollections.observableArrayList(((Source)arg0.getValue()).getSourceElements());				
					tblSourceElements.getItems().clear();
					tblSourceElements.setItems(sourceElements);
				}
			}
		});

		//Source Add button
		btnSourcesAdd.setOnAction(new EventHandler() {			 
			@Override
			public void handle(Event arg0) { 
				if(jpsC != null){
					Source newS = new Source("[Type]", new ArrayList<SourceElement>(), "[Method]", "[Description]");
					tblSources.getItems().add(newS);
					tblSources.getSelectionModel().selectLast();
					saved = false;
				}
			}
		});
		//Source Delete button
		btnSourcesDelete.setOnAction(new EventHandler() {			 
			@Override
			public void handle(Event arg0) { 
				if(jpsC != null){

					int index = tblSources.getSelectionModel().getSelectedIndex();
					if(index >= 0){
						if(Dialogs.showConfirmDialog(null, "Are you sure you want to delete this Source?", "Confirmation", "JPSearch Editor", DialogOptions.YES_NO) == DialogResponse.YES){
							tblSources.getItems().remove(index);
							tblSourceElements.getItems().clear();
							tblSources.getSelectionModel().selectNext();
							saved = false;
						}
					}
					else{
						Dialogs.showWarningDialog(null, "No items were selected" , "Warning", "JPSearch Editor");
					}
				}
			}
		});

		//SourceElements Add button
		btnSourceElementsAdd.setOnAction(new EventHandler() {			 
			@Override
			public void handle(Event arg0) { 
				if(tblSources.getSelectionModel().getSelectedItem() != null){
					SourceElement newSE = new SourceElement("[Title]","[Description]","[Identifier]");
					Source s = (Source) tblSources.getSelectionModel().getSelectedItem();
					s.getSourceElements().add(newSE); //have to add to the original Object as well
					tblSourceElements.getItems().add(newSE);
					tblSourceElements.getSelectionModel().selectLast();   
					saved = false;
				}
				else{
					Dialogs.showWarningDialog(null, "Please select a Source first" , "Warning", "JPSearch Editor");
				}

			}
		});
		//SourceElements Delete button
		btnSourceElementsDelete.setOnAction(new EventHandler() {			 
			@Override
			public void handle(Event arg0) { 
				if(tblSources.getSelectionModel().getSelectedItem() != null){
					int index = tblSourceElements.getSelectionModel().getSelectedIndex();
					if(index >= 0){
						if(Dialogs.showConfirmDialog(null, "Are you sure you want to delete this Source Element?", "Confirmation", "JPSearch Editor", DialogOptions.YES_NO) == DialogResponse.YES){
							Source s = (Source) tblSources.getSelectionModel().getSelectedItem();
							SourceElement se = (SourceElement) tblSourceElements.getSelectionModel().getSelectedItem();
							s.getSourceElements().remove(se); //have to remove from the original Object as well
							tblSourceElements.getItems().remove(index);   
							tblSourceElements.getSelectionModel().selectNext();
							saved = false;
						}	
					}
					else{
						Dialogs.showWarningDialog(null, "Please select an item first" , "Warning", "JPSearch Editor"); 
					}
				}
				else{
					Dialogs.showWarningDialog(null, "Please select a Source first" , "Warning", "JPSearch Editor"); 
				}
			}
		});

	}

	/**
	 * Initializes the Region of interest Section 
	 */

	public void initializeRegionOfInterest(){

		tblRegionOfInterest.getColumns().clear();
		tblRegionOfInterest.getColumns().addAll(roiTitle, roiDescription);
		tblRegionOfInterest.getSelectionModel().setSelectionMode(SelectionMode.SINGLE);
		roiTitle.setCellValueFactory(new PropertyValueFactory<RegionOfInterest,String>("Title"));
		roiTitle.prefWidthProperty().bind(tblRegionOfInterest.widthProperty().divide(2));
		roiTitle.setEditable(true);
		roiDescription.setCellValueFactory(new PropertyValueFactory<RegionOfInterest,String>("Description"));
		roiDescription.prefWidthProperty().bind(tblRegionOfInterest.widthProperty().divide(2));
		roiDescription.setEditable(true);

		/*
		 * External Description section
		 */
		tblExternalDescription.getColumns().clear();
		tblExternalDescription.getColumns().addAll(tnFromNameSpace, tnFromNameSpacePrefix, lvFromControlledVocabularyOfLiterals,
				lvFromGrammarOfLiterals, rvFromNameSpace, svFromNameSpace);
		tblExternalDescription.getSelectionModel().setSelectionMode(SelectionMode.SINGLE);

		tnFromNameSpace.setCellValueFactory(new Callback<CellDataFeatures<ExtDescription, String>, ObservableValue<String>>() {
			public ObservableValue<String> call(CellDataFeatures<ExtDescription, String> p) {
				if(p.getValue().getTnFromNameSpace() == null)
					return new ReadOnlyObjectWrapper("");
				return new ReadOnlyObjectWrapper(p.getValue().getTnFromNameSpace());
			}
		});

		tnFromNameSpacePrefix.setCellValueFactory(new Callback<CellDataFeatures<ExtDescription, String>, ObservableValue<String>>() {
			public ObservableValue<String> call(CellDataFeatures<ExtDescription, String> p) {
				if(p.getValue().getTnFromNameSpacePrefix() == null)
					return new ReadOnlyObjectWrapper("");
				return new ReadOnlyObjectWrapper(p.getValue().getTnFromNameSpacePrefix());
			}
		});

		lvFromControlledVocabularyOfLiterals.setCellValueFactory(new Callback<CellDataFeatures<ExtDescription, String>, ObservableValue<String>>() {
			public ObservableValue<String> call(CellDataFeatures<ExtDescription, String> p) {
				if(p.getValue().getLvFromControlledVocabularyOfLiterals() == null)
					return new ReadOnlyObjectWrapper("");
				return new ReadOnlyObjectWrapper(p.getValue().getLvFromControlledVocabularyOfLiterals());
			}
		});

		lvFromGrammarOfLiterals.setCellValueFactory(new Callback<CellDataFeatures<ExtDescription, String>, ObservableValue<String>>() {
			public ObservableValue<String> call(CellDataFeatures<ExtDescription, String> p) {
				if(p.getValue().getLvFromGrammarOfLiterals() == null)
					return new ReadOnlyObjectWrapper("");
				return new ReadOnlyObjectWrapper(p.getValue().getLvFromGrammarOfLiterals());
			}
		});

		rvFromNameSpace.setCellValueFactory(new Callback<CellDataFeatures<ExtDescription, String>, ObservableValue<String>>() {
			public ObservableValue<String> call(CellDataFeatures<ExtDescription, String> p) {
				if(p.getValue().getRvFromNameSpace() == null)
					return new ReadOnlyObjectWrapper("");
				return new ReadOnlyObjectWrapper(p.getValue().getRvFromNameSpace());
			}
		});

		svFromNameSpace.setCellValueFactory(new Callback<CellDataFeatures<ExtDescription, String>, ObservableValue<String>>() {
			public ObservableValue<String> call(CellDataFeatures<ExtDescription, String> p) {
				if(p.getValue().getSvFromNameSpace() == null)
					return new ReadOnlyObjectWrapper("");
				return new ReadOnlyObjectWrapper(p.getValue().getSvFromNameSpace());
			}
		});

		tnFromNameSpace.prefWidthProperty().bind(tblExternalDescription.widthProperty().divide(6)); 
		tnFromNameSpacePrefix.prefWidthProperty().bind(tblExternalDescription.widthProperty().divide(6)); 
		lvFromControlledVocabularyOfLiterals.prefWidthProperty().bind(tblExternalDescription.widthProperty().divide(6));
		lvFromGrammarOfLiterals.prefWidthProperty().bind(tblExternalDescription.widthProperty().divide(6)); 
		rvFromNameSpace.prefWidthProperty().bind(tblExternalDescription.widthProperty().divide(6));
		svFromNameSpace.prefWidthProperty().bind(tblExternalDescription.widthProperty().divide(6));

		tnFromNameSpace.setEditable(true);
		tnFromNameSpacePrefix.setEditable(true); 
		lvFromControlledVocabularyOfLiterals.setEditable(true);
		lvFromGrammarOfLiterals.setEditable(true); 
		rvFromNameSpace.setEditable(true);
		svFromNameSpace.setEditable(true);

		tnFromNameSpace.setText("Tag Name FNS");
		tnFromNameSpacePrefix.setText("Tag Name FNSP");
		lvFromControlledVocabularyOfLiterals.setText("Literal Value CVL");
		lvFromGrammarOfLiterals.setText("Literal Value GL");
		rvFromNameSpace.setText("Resource Value FNS");
		svFromNameSpace.setText("Structure Value FNS");
		//---------------------------------------

		tblROIPerson.getColumns().clear();
		tblROIPerson.getColumns().addAll(roiPersonGivenName, roiPersonFamilyName,
				roiPersonDescription, roiPersonNationality, roiPersonAddressName, roiPersonAddressDescription);
		tblROIPerson.getSelectionModel().setSelectionMode(SelectionMode.SINGLE);		

		roiPersonGivenName.setCellValueFactory(new Callback<CellDataFeatures<CDPerson, String>, ObservableValue<String>>() {
			public ObservableValue<String> call(CellDataFeatures<CDPerson, String> p) {
				return new ReadOnlyObjectWrapper(p.getValue().getNames().getGivenName());
			}
		});	 

		roiPersonGivenName.prefWidthProperty().bind(tblROIPerson.widthProperty().divide(6));
		roiPersonGivenName.setEditable(true);

		roiPersonFamilyName.setCellValueFactory(new Callback<CellDataFeatures<CDPerson, String>, ObservableValue<String>>() {
			public ObservableValue<String> call(CellDataFeatures<CDPerson, String> p) {
				return new ReadOnlyObjectWrapper(p.getValue().getNames().getFamilyName());
			}
		});
		roiPersonFamilyName.prefWidthProperty().bind(tblROIPerson.widthProperty().divide(6));
		roiPersonFamilyName.setEditable(true);

		roiPersonDescription.setCellValueFactory(new Callback<CellDataFeatures<CDPerson, String>, ObservableValue<String>>() {
			public ObservableValue<String> call(CellDataFeatures<CDPerson, String> p) {
				return new ReadOnlyObjectWrapper(p.getValue().getDescription());
			}
		});
		roiPersonDescription.prefWidthProperty().bind(tblROIPerson.widthProperty().divide(6));
		roiPersonDescription.setEditable(true);

		roiPersonNationality.setCellValueFactory(new Callback<CellDataFeatures<CDPerson, String>, ObservableValue<String>>() {
			public ObservableValue<String> call(CellDataFeatures<CDPerson, String> p) {
				return new ReadOnlyObjectWrapper(p.getValue().getNationality());
			}
		});
		roiPersonNationality.prefWidthProperty().bind(tblROIPerson.widthProperty().divide(6));
		roiPersonNationality.setEditable(true);		

		roiPersonAddressName.setCellValueFactory(new Callback<CellDataFeatures<CDPerson, String>, ObservableValue<String>>() {
			public ObservableValue<String> call(CellDataFeatures<CDPerson, String> p) {
				return new ReadOnlyObjectWrapper(p.getValue().getAddress().getAddressNameFull());
			}
		});
		roiPersonAddressName.prefWidthProperty().bind(tblROIPerson.widthProperty().divide(6));
		roiPersonAddressName.setEditable(true);	

		roiPersonAddressDescription.setCellValueFactory(new Callback<CellDataFeatures<CDPerson, String>, ObservableValue<String>>() {
			public ObservableValue<String> call(CellDataFeatures<CDPerson, String> p) {
				return new ReadOnlyObjectWrapper(p.getValue().getAddress().getAddressDescriptionFull());
			}
		});
		roiPersonAddressDescription.prefWidthProperty().bind(tblROIPerson.widthProperty().divide(6));
		roiPersonAddressDescription.setEditable(true);	

		//------ Affiliations ----------
		tblROIPersonAffiliation.getColumns().clear();
		tblROIPersonAffiliation.getColumns().addAll(roiPersonAffNames);
		tblROIPersonAffiliation.getSelectionModel().setSelectionMode(SelectionMode.SINGLE);	
		roiPersonAffNames.setCellValueFactory(new Callback<CellDataFeatures<CDOrganizationType, String>, ObservableValue<String>>() {
			public ObservableValue<String> call(CellDataFeatures<CDOrganizationType, String> p) {
				List<String> names = p.getValue().getNames();
				String s = StringUtils.join(names, " ");
				return new ReadOnlyObjectWrapper(s);
			}
		});
		roiPersonAffNames.prefWidthProperty().bind(tblROIPersonAffiliation.widthProperty().divide(1));
		roiPersonAffNames.setEditable(true);

		tblROIObject.getColumns().clear();
		tblROIObject.getColumns().addAll(roiObjectName, roiObjectDesc, roiObjectLabel);
		tblROIObject.getSelectionModel().setSelectionMode(SelectionMode.SINGLE);	

		roiObjectName.setCellValueFactory(new Callback<CellDataFeatures<CDObject, String>, ObservableValue<String>>() {
			public ObservableValue<String> call(CellDataFeatures<CDObject, String> p) {
				List<String> names = p.getValue().getName();
				String s = StringUtils.join(names, " ");
				return new ReadOnlyObjectWrapper(s);
			}
		});
		roiObjectName.prefWidthProperty().bind(tblROIObject.widthProperty().divide(3));
		roiObjectName.setEditable(true);

		roiObjectDesc.setCellValueFactory(new Callback<CellDataFeatures<CDObject, String>, ObservableValue<String>>() {
			public ObservableValue<String> call(CellDataFeatures<CDObject, String> p) {
				return new ReadOnlyObjectWrapper(p.getValue().getDescription());
			}
		});
		roiObjectDesc.prefWidthProperty().bind(tblROIObject.widthProperty().divide(3));
		roiObjectDesc.setEditable(true);

		roiObjectLabel.setCellValueFactory(new Callback<CellDataFeatures<CDObject, String>, ObservableValue<String>>() {
			public ObservableValue<String> call(CellDataFeatures<CDObject, String> p) {
				return new ReadOnlyObjectWrapper(p.getValue().getLabel());
			}
		});
		roiObjectLabel.prefWidthProperty().bind(tblROIObject.widthProperty().divide(3));
		roiObjectLabel.setEditable(true);

		//Place
		tblROIPlace.getColumns().clear();
		tblROIPlace.getColumns().addAll(roiPlaceName, roiPlaceDesc);
		tblROIPlace.getSelectionModel().setSelectionMode(SelectionMode.SINGLE);

		roiPlaceName.prefWidthProperty().bind(tblROIPlace.widthProperty().divide(2));
		roiPlaceName.setEditable(true);

		roiPlaceName.setCellValueFactory(new Callback<CellDataFeatures<Place, String>, ObservableValue<String>>() {
			public ObservableValue<String> call(CellDataFeatures<Place, String> p) {
				List<String> names = p.getValue().getName();
				String s = StringUtils.join(names, " ");
				return new ReadOnlyObjectWrapper(s);
			}
		});

		roiPlaceDesc.prefWidthProperty().bind(tblROIPlace.widthProperty().divide(2));
		roiPlaceDesc.setEditable(true);

		roiPlaceDesc.setCellValueFactory(new Callback<CellDataFeatures<Place, String>, ObservableValue<String>>() {
			public ObservableValue<String> call(CellDataFeatures<Place, String> p) {
				List<String> desc = p.getValue().getDescription();
				String s = StringUtils.join(desc, " ");
				return new ReadOnlyObjectWrapper(s);
			}
		});

		//Event
		tblROIEvent.getColumns().clear();
		tblROIEvent.getColumns().addAll(roiEventLabel, roiEventDesc);
		tblROIEvent.getSelectionModel().setSelectionMode(SelectionMode.SINGLE);

		roiEventLabel.prefWidthProperty().bind(tblROIEvent.widthProperty().divide(2));
		roiEventLabel.setEditable(true);

		roiEventLabel.setCellValueFactory(new Callback<CellDataFeatures<CDEvent, String>, ObservableValue<String>>() {
			public ObservableValue<String> call(CellDataFeatures<CDEvent, String> p) {
				List<String> names = p.getValue().getLabel();
				String s = StringUtils.join(names, " ");
				return new ReadOnlyObjectWrapper(s);
			}
		});

		roiEventDesc.prefWidthProperty().bind(tblROIEvent.widthProperty().divide(2));
		roiEventDesc.setEditable(true);

		roiEventDesc.setCellValueFactory(new Callback<CellDataFeatures<CDEvent, String>, ObservableValue<String>>() {
			public ObservableValue<String> call(CellDataFeatures<CDEvent, String> p) {
				List<String> names = p.getValue().getDescription();
				String s = StringUtils.join(names, " ");
				return new ReadOnlyObjectWrapper(s);
			}
		});

		Callback<TableColumn, TableCell> cellFactoryRegionOfInterest = new Callback<TableColumn, TableCell>() {
			public TableCell call(TableColumn p) {
				return new EditingCell<RegionOfInterest>();
			}
		};

		Callback<TableColumn, TableCell> cellFactoryExtDescripion = new Callback<TableColumn, TableCell>() {
			public TableCell call(TableColumn p) {
				return new EditingCell<ExtDescription>();
			}
		};

		Callback<TableColumn, TableCell> cellFactoryCDPerson = new Callback<TableColumn, TableCell>() {
			public TableCell call(TableColumn p) {
				return new EditingCell<CDPerson>();
			}
		};

		Callback<TableColumn, TableCell> cellFactoryCDOrganizationType = new Callback<TableColumn, TableCell>() {
			public TableCell call(TableColumn p) {
				return new EditingCell<CDOrganizationType>();
			}
		};		

		Callback<TableColumn, TableCell> cellFactoryCDObject = new Callback<TableColumn, TableCell>() {
			public TableCell call(TableColumn p) {
				return new EditingCell<CDObject>();
			}
		};
		Callback<TableColumn, TableCell> cellFactoryPlace = new Callback<TableColumn, TableCell>() {
			public TableCell call(TableColumn p) {
				return new EditingCell<Place>();
			}
		};
		Callback<TableColumn, TableCell> cellFactoryCDEvent = new Callback<TableColumn, TableCell>() {
			public TableCell call(TableColumn p) {
				return new EditingCell<CDEvent>();
			}
		};
		Callback<TableColumn, TableCell> anyURIFactory = new Callback<TableColumn, TableCell>() {
			@Override
			public TableCell call(TableColumn p) {
				return new AnyUriEditableTableCell();
			}
		};

		roiTitle.setCellFactory(cellFactoryRegionOfInterest);
		roiDescription.setCellFactory(cellFactoryRegionOfInterest);

		tnFromNameSpace.setCellFactory(anyURIFactory);
		tnFromNameSpacePrefix.setCellFactory(cellFactoryExtDescripion);
		lvFromControlledVocabularyOfLiterals.setCellFactory(cellFactoryExtDescripion);
		lvFromGrammarOfLiterals.setCellFactory(anyURIFactory);
		rvFromNameSpace.setCellFactory(anyURIFactory);
		svFromNameSpace.setCellFactory(anyURIFactory);

		roiPersonGivenName.setCellFactory(cellFactoryCDPerson);		
		roiPersonFamilyName.setCellFactory(cellFactoryCDPerson);
		roiPersonDescription.setCellFactory(cellFactoryCDPerson);
		roiPersonNationality.setCellFactory(cellFactoryCDPerson);
		roiPersonAddressName.setCellFactory(cellFactoryCDPerson);
		roiPersonAddressDescription.setCellFactory(cellFactoryCDPerson);

		roiPersonAffNames.setCellFactory(cellFactoryCDOrganizationType);

		roiObjectName.setCellFactory(cellFactoryCDObject);
		roiObjectDesc.setCellFactory(cellFactoryCDObject);
		roiObjectLabel.setCellFactory(anyURIFactory);

		roiPlaceName.setCellFactory(cellFactoryPlace);
		roiPlaceDesc.setCellFactory(cellFactoryPlace);

		roiEventLabel.setCellFactory(anyURIFactory);
		roiEventDesc.setCellFactory(cellFactoryCDEvent);

		//Region of Interest
		roiTitle.setOnEditCommit(
				new EventHandler<TableColumn.CellEditEvent<RegionOfInterest, String>>() {
					@Override
					public void handle(TableColumn.CellEditEvent<RegionOfInterest, String> t) {
						int rowSelected = t.getTablePosition().getRow();
						(t.getTableView().getItems().get(rowSelected)).setTitle(t.getNewValue());
						updateExternalDescription();
						saved = false;
					}
				});
		roiDescription.setOnEditCommit(
				new EventHandler<TableColumn.CellEditEvent<RegionOfInterest, String>>() {
					@Override
					public void handle(TableColumn.CellEditEvent<RegionOfInterest, String> t) {
						int rowSelected = t.getTablePosition().getRow();
						(t.getTableView().getItems().get(rowSelected)).setDescription(t.getNewValue());
						updateExternalDescription();
						saved = false;
					}
				});

		tnFromNameSpace.setOnEditCommit(
				new EventHandler<TableColumn.CellEditEvent<ExtDescription, String>>() {
					@Override
					public void handle(TableColumn.CellEditEvent<ExtDescription, String> t) {
						int rowSelected = t.getTablePosition().getRow();
						(t.getTableView().getItems().get(rowSelected)).setTnFromNameSpace(t.getNewValue());
						updateExternalDescription();
						saved = false;
					}
				});
		tnFromNameSpacePrefix.setOnEditCommit(
				new EventHandler<TableColumn.CellEditEvent<ExtDescription, String>>() {
					@Override
					public void handle(TableColumn.CellEditEvent<ExtDescription, String> t) {
						int rowSelected = t.getTablePosition().getRow();
						(t.getTableView().getItems().get(rowSelected)).setTnFromNameSpacePrefix(t.getNewValue());
						updateExternalDescription();
						saved = false;
					}
				});
		lvFromControlledVocabularyOfLiterals.setOnEditCommit(
				new EventHandler<TableColumn.CellEditEvent<ExtDescription, String>>() {
					@Override
					public void handle(TableColumn.CellEditEvent<ExtDescription, String> t) {
						int rowSelected = t.getTablePosition().getRow();
						(t.getTableView().getItems().get(rowSelected)).setLvFromControlledVocabularyOfLiterals(t.getNewValue());
						updateExternalDescription();
						saved = false;
					}
				});
		lvFromGrammarOfLiterals.setOnEditCommit(
				new EventHandler<TableColumn.CellEditEvent<ExtDescription, String>>() {
					@Override
					public void handle(TableColumn.CellEditEvent<ExtDescription, String> t) {
						int rowSelected = t.getTablePosition().getRow();
						(t.getTableView().getItems().get(rowSelected)).setLvFromGrammarOfLiterals(t.getNewValue());
						updateExternalDescription();
						saved = false;
					}
				});
		rvFromNameSpace.setOnEditCommit(
				new EventHandler<TableColumn.CellEditEvent<ExtDescription, String>>() {
					@Override
					public void handle(TableColumn.CellEditEvent<ExtDescription, String> t) {
						int rowSelected = t.getTablePosition().getRow();
						(t.getTableView().getItems().get(rowSelected)).setRvFromNameSpace(t.getNewValue());
						updateExternalDescription();
						saved = false;
					}
				});
		svFromNameSpace.setOnEditCommit(
				new EventHandler<TableColumn.CellEditEvent<ExtDescription, String>>() {
					@Override
					public void handle(TableColumn.CellEditEvent<ExtDescription, String> t) {
						int rowSelected = t.getTablePosition().getRow();
						(t.getTableView().getItems().get(rowSelected)).setSvFromNameSpace(t.getNewValue());
						updateExternalDescription();
						saved = false;
					}
				});

		roiPersonGivenName.setOnEditCommit(
				new EventHandler<TableColumn.CellEditEvent<CDPerson, String>>() {
					@Override
					public void handle(TableColumn.CellEditEvent<CDPerson, String> t) {
						int rowSelected = t.getTablePosition().getRow();
						(t.getTableView().getItems().get(rowSelected)).setGivenName(t.getNewValue());
						saved = false;
					}
				});
		roiPersonFamilyName.setOnEditCommit(
				new EventHandler<TableColumn.CellEditEvent<CDPerson, String>>() {
					@Override
					public void handle(TableColumn.CellEditEvent<CDPerson, String> t) {
						int rowSelected = t.getTablePosition().getRow();
						(t.getTableView().getItems().get(rowSelected)).setFamilyName(t.getNewValue());
						saved = false;
					}
				});
		roiPersonDescription.setOnEditCommit(
				new EventHandler<TableColumn.CellEditEvent<CDPerson, String>>() {
					@Override
					public void handle(TableColumn.CellEditEvent<CDPerson, String> t) {
						int rowSelected = t.getTablePosition().getRow();
						(t.getTableView().getItems().get(rowSelected)).setFamilyName(t.getNewValue());
						saved = false;
					}
				});
		roiPersonNationality.setOnEditCommit(
				new EventHandler<TableColumn.CellEditEvent<CDPerson, String>>() {
					@Override
					public void handle(TableColumn.CellEditEvent<CDPerson, String> t) {
						int rowSelected = t.getTablePosition().getRow();
						(t.getTableView().getItems().get(rowSelected)).setNationality(t.getNewValue());
						saved = false;
					}
				});
		roiPersonAddressName.setOnEditCommit(
				new EventHandler<TableColumn.CellEditEvent<CDPerson, String>>() {
					@Override
					public void handle(TableColumn.CellEditEvent<CDPerson, String> t) {
						int rowSelected = t.getTablePosition().getRow();
						(t.getTableView().getItems().get(rowSelected)).setAddressName(t.getNewValue());
						saved = false;
					}
				});
		roiPersonAddressDescription.setOnEditCommit(
				new EventHandler<TableColumn.CellEditEvent<CDPerson, String>>() {
					@Override
					public void handle(TableColumn.CellEditEvent<CDPerson, String> t) {
						int rowSelected = t.getTablePosition().getRow();
						(t.getTableView().getItems().get(rowSelected)).setAddressDescription(t.getNewValue());
						saved = false;
					}
				});
		roiPersonAffNames.setOnEditCommit(
				new EventHandler<TableColumn.CellEditEvent<CDOrganizationType, String>>() {
					@Override
					public void handle(TableColumn.CellEditEvent<CDOrganizationType, String> t) {
						int rowSelected = t.getTablePosition().getRow();
						(t.getTableView().getItems().get(rowSelected)).getNames().clear();
						(t.getTableView().getItems().get(rowSelected)).getNames().add(t.getNewValue());
						saved = false;
					}
				});

		roiObjectName.setOnEditCommit(
				new EventHandler<TableColumn.CellEditEvent<CDObject, String>>() {
					@Override
					public void handle(TableColumn.CellEditEvent<CDObject, String> t) {
						int rowSelected = t.getTablePosition().getRow();
						(t.getTableView().getItems().get(rowSelected)).getName().clear();
						(t.getTableView().getItems().get(rowSelected)).getName().add(t.getNewValue());
						saved = false;
					}
				});

		roiObjectDesc.setOnEditCommit(
				new EventHandler<TableColumn.CellEditEvent<CDObject, String>>() {
					@Override
					public void handle(TableColumn.CellEditEvent<CDObject, String> t) {
						int rowSelected = t.getTablePosition().getRow();
						(t.getTableView().getItems().get(rowSelected)).setDescription(t.getNewValue());
						saved = false;
					}
				});
		roiObjectLabel.setOnEditCommit(
				new EventHandler<TableColumn.CellEditEvent<CDObject, String>>() {
					@Override
					public void handle(TableColumn.CellEditEvent<CDObject, String> t) {
						int rowSelected = t.getTablePosition().getRow();
						(t.getTableView().getItems().get(rowSelected)).setLabel(t.getNewValue());
						saved = false;
					}
				});

		roiPlaceName.setOnEditCommit(
				new EventHandler<TableColumn.CellEditEvent<Place, String>>() {
					@Override
					public void handle(TableColumn.CellEditEvent<Place, String> t) {
						int rowSelected = t.getTablePosition().getRow();
						(t.getTableView().getItems().get(rowSelected)).getName().clear();
						(t.getTableView().getItems().get(rowSelected)).getName().add(t.getNewValue());
						saved = false;
					}
				});
		roiPlaceDesc.setOnEditCommit(
				new EventHandler<TableColumn.CellEditEvent<Place, String>>() {
					@Override
					public void handle(TableColumn.CellEditEvent<Place, String> t) {
						int rowSelected = t.getTablePosition().getRow();
						(t.getTableView().getItems().get(rowSelected)).getDescription().clear();
						(t.getTableView().getItems().get(rowSelected)).getDescription().add(t.getNewValue());
						saved = false;
					}
				});
		roiEventDesc.setOnEditCommit(
				new EventHandler<TableColumn.CellEditEvent<CDEvent, String>>() {
					@Override
					public void handle(TableColumn.CellEditEvent<CDEvent, String> t) {
						int rowSelected = t.getTablePosition().getRow();
						(t.getTableView().getItems().get(rowSelected)).getDescription().clear();
						(t.getTableView().getItems().get(rowSelected)).getDescription().add(t.getNewValue());
						saved = false;
					}
				});
		roiEventLabel.setOnEditCommit(
				new EventHandler<TableColumn.CellEditEvent<CDEvent, String>>() {
					@Override
					public void handle(TableColumn.CellEditEvent<CDEvent, String> t) {
						int rowSelected = t.getTablePosition().getRow();
						(t.getTableView().getItems().get(rowSelected)).getLabel().clear();
						(t.getTableView().getItems().get(rowSelected)).getLabel().add(t.getNewValue());
						saved = false;
					}
				});		

		//Row clicking in Sources table event
		tblRegionOfInterest.getSelectionModel().selectedItemProperty().addListener(new ChangeListener() {

			@Override
			public void changed(ObservableValue arg0, Object arg1, Object arg2) {
				if(arg0.getValue() != null){
					RegionOfInterest roiSel = ((RegionOfInterest)arg0.getValue());

					//Keywords
					ObservableList<String> listKeywords = FXCollections.observableArrayList(roiSel.getKeywords());
					lstROIKeywords.setItems(listKeywords);

					//Region Locator
					lstRegionLocator.getItems().clear();					
					RegionLocator rl = roiSel.getRegionLocator();
					for(Region r : rl.getRegions()){
						String reg = "";
						for(Integer i : r.getArea()){
							reg += i + " ";
						}	
						lstRegionLocator.getItems().add(reg);
					}

					setSnapshot(roiSel);

					//External Description
					List<ExternalDescription> ed = roiSel.getExternalDescription();
					List<ExtDescription> extDes = new ArrayList<ExtDescription>();
					for(ExternalDescription e : ed){
						extDes.add(new ExtDescription(e));
					}
					ObservableList<ExtDescription> olExtDescr = FXCollections.observableArrayList(extDes);
					tblExternalDescription.getItems().clear();
					tblExternalDescription.setItems(olExtDescr);

					//Content Description
					if(roiSel.getContentDescription() != null){
						ObservableList<CDPerson> p = FXCollections.observableArrayList(roiSel.getContentDescription().getPerson());
						tblROIPerson.getItems().clear();
						tblROIPerson.setItems(p); 

						ObservableList<CDObject> o = FXCollections.observableArrayList(roiSel.getContentDescription().getObject());
						tblROIObject.getItems().clear();
						tblROIObject.setItems(o);

						ObservableList<Place> pl = FXCollections.observableArrayList(roiSel.getContentDescription().getPlace());
						tblROIPlace.getItems().clear();
						tblROIPlace.setItems(pl);

						ObservableList<CDEvent> e = FXCollections.observableArrayList(roiSel.getContentDescription().getEvent());
						tblROIEvent.getItems().clear();
						tblROIEvent.setItems(e);
					}

					tblROIPersonAffiliation.getItems().clear();
					txaPersonAffiliationAddressName.setText("");
					txaPersonAffiliationAddressDesc.setText("");
				}
			}
		});
		tblROIPerson.getSelectionModel().selectedItemProperty().addListener( new ChangeListener() {

			@Override
			public void changed(ObservableValue arg0, Object arg1, Object arg2) {
				if(arg0.getValue() != null){
					CDPerson person = (CDPerson)arg0.getValue();
					ObservableList<CDOrganizationType> p = FXCollections.observableArrayList(person.getAffiliation());
					tblROIPersonAffiliation.getItems().clear();
					tblROIPersonAffiliation.setItems(p);

					txaPersonAffiliationAddressName.setText("");
					txaPersonAffiliationAddressDesc.setText("");
				}
			}
		});

		tblROIPersonAffiliation.getSelectionModel().selectedItemProperty().addListener( new ChangeListener() {
			@Override
			public void changed(ObservableValue arg0, Object arg1, Object arg2) {
				if(arg0.getValue() != null){
					List<String> s = ((CDOrganizationType)arg0.getValue()).getAddress().getName();
					String r = "";
					for(String n : s){
						r += n + "\n";
					}
					txaPersonAffiliationAddressName.setText(r);

					List<String> s2 = ((CDOrganizationType)arg0.getValue()).getAddress().getDescription();
					String r2 = "";
					for(String n : s2){
						r2 += n + "\n";
					}
					txaPersonAffiliationAddressDesc.setText(r2);
				}
			}
		});

		txtAddKeywordROI.setOnKeyPressed(new EventHandler<KeyEvent>() {
			public void handle(KeyEvent ke) {		        
				if(ke.getCode() == KeyCode.ENTER){
					handleAddToRoiKeywords();
				}
			}
		});

		//External Description updates
		txaTNContent.focusedProperty().addListener(new ChangeListener<Boolean>() {
			@Override
			public void changed(ObservableValue<? extends Boolean> arg0, Boolean arg1, Boolean arg2) {
				// Handling only when focus is out.
				if(!arg2 && tblExternalDescription.getSelectionModel().getSelectedItem() != null){
					ExtDescription ed = (ExtDescription)tblExternalDescription.getSelectionModel().getSelectedItem();
					String c = txaTNContent.getText();
					ed.setTnContent(c);
					updateExternalDescription();
					saved = false;
				}
			}
		});

		txaLVContent.focusedProperty().addListener(new ChangeListener<Boolean>() {
			@Override
			public void changed(ObservableValue<? extends Boolean> arg0, Boolean arg1, Boolean arg2) {
				// Handling only when focus is out.
				if(!arg2 && tblExternalDescription.getSelectionModel().getSelectedItem() != null){
					ExtDescription ed = (ExtDescription)tblExternalDescription.getSelectionModel().getSelectedItem();
					String c = txaLVContent.getText();
					ed.setLvContent(c);
					updateExternalDescription();
					saved = false;
				}
			}
		});
		txaRVContent.focusedProperty().addListener(new ChangeListener<Boolean>() {
			@Override
			public void changed(ObservableValue<? extends Boolean> arg0, Boolean arg1, Boolean arg2) {
				// Handling only when focus is out.
				if(!arg2 && tblExternalDescription.getSelectionModel().getSelectedItem() != null){
					ExtDescription ed = (ExtDescription)tblExternalDescription.getSelectionModel().getSelectedItem();
					String c = txaRVContent.getText();
					ed.setRvContent(c);
					updateExternalDescription();
					saved = false;
				}
			}
		});

		txaSVContent.focusedProperty().addListener(new ChangeListener<Boolean>() {
			@Override
			public void changed(ObservableValue<? extends Boolean> arg0, Boolean arg1, Boolean arg2) {
				// Handling only when focus is out.
				if(!arg2 && tblExternalDescription.getSelectionModel().getSelectedItem() != null){
					ExtDescription ed = (ExtDescription)tblExternalDescription.getSelectionModel().getSelectedItem();
					String c = txaSVContent.getText();
					ed.setSvContent(c);
					updateExternalDescription();
					saved = false;
				}
			}
		});

		//Row clicking in External Description table event
		tblExternalDescription.getSelectionModel().selectedItemProperty().addListener( new ChangeListener() {
			@Override
			public void changed(ObservableValue arg0, Object arg1, Object arg2) {
				if(arg0.getValue() != null){
					ExtDescription selED = (ExtDescription)arg0.getValue();
					txaTNContent.setText(selED.getTnContent());
					txaLVContent.setText(selED.getLvContent());
					txaRVContent.setText(selED.getRvContent());
					txaSVContent.setText(selED.getSvContent().toString());
				}
			}
		});
		tblExternalDescription.getColumns().addListener(new ListChangeListener() {
			public boolean suspended;

			@Override
			public void onChanged(Change change) {
				change.next();
				if (change.wasReplaced() && !suspended) {
					this.suspended = true;
					this.suspended = false;
				}
			}
		});
		btnEDAdd.setOnAction(new EventHandler() {			 
			@Override
			public void handle(Event arg0) { 
				if(tblRegionOfInterest.getSelectionModel().getSelectedItem() != null){
					ExtDescription newED = new ExtDescription(
							"[FromNameSpace]", 
							"[FromNameSpacePrefix]",
							"",
							"[FromControlledVocabularyOfLiterals]", 
							"[FromGrammarOfLiterals]",
							"",
							"[FromNameSpace]",
							"", 
							"[FromNameSpace]", 
							"");

					RegionOfInterest roi = tblRegionOfInterest.getSelectionModel().getSelectedItem();
					roi.getExternalDescription().add(new ExternalDescription(newED));//have to add to the original Object as well

					tblExternalDescription.getItems().add(newED);
					tblExternalDescription.getSelectionModel().selectLast();
					saved = false;

				}
				else{
					Dialogs.showErrorDialog(null, "None Region of Interest was selected" , "Error", "JPSearch Editor"); 
				}
			}
		});
		btnEDDelete.setOnAction(new EventHandler() {			 
			@Override
			public void handle(Event arg0) { 
				if(tblExternalDescription.getSelectionModel().getSelectedItem() != null){

					int index = tblExternalDescription.getSelectionModel().getSelectedIndex();
					if(index >= 0){
						if(Dialogs.showConfirmDialog(null, "Are you sure you want to delete this External Description?", "Confirmation", "JPSearch Editor", DialogOptions.YES_NO) == DialogResponse.YES){

							RegionOfInterest roi = tblRegionOfInterest.getSelectionModel().getSelectedItem();
							roi.getExternalDescription().remove(index); //have to remove from the original Object as well

							tblExternalDescription.getItems().remove(index);   
							tblExternalDescription.getSelectionModel().selectNext();
							updateExternalDescription();
							if(tblExternalDescription.getItems().size() == 0){
								txaTNContent.setText("");
								txaLVContent.setText("");
								txaRVContent.setText("");
								txaSVContent.setText("");
							}
							saved = false;
						}
					}
					else{
						Dialogs.showWarningDialog(null, "No item selected" , "Warning", "JPSearch Editor");
					}
				}
				else{
					Dialogs.showErrorDialog(null, "None External Description was selected" , "Error", "JPSearch Editor"); 
				}
			}
		});
		btnAddStructuredValue.setOnAction(new EventHandler() { 
			@Override
			public void handle(Event arg0) { 
				if(tblExternalDescription.getItems().isEmpty()){
					Dialogs.showWarningDialog(null, "The table has no items" , "Warning", "JPSearch Editor"); 
					return;
				}

				if(tblExternalDescription.getSelectionModel().getSelectedItem() != null){

					ExtDescription thisED = tblExternalDescription.getSelectionModel().getSelectedItem();
					if(thisED.getSvFromNameSpace().equals("")){
						Dialogs.showWarningDialog(null, "In order to add a structure value, the 'Structured Value FromNameSpace' must " +
								"be set." , "Warning", "JPSearch Editor"); 
						return;
					}

					FileChooser fileChooser = new FileChooser();

					//Set extension filter
					FileChooser.ExtensionFilter extFilter = new FileChooser.ExtensionFilter("XML files (*.xml)", "*.xml");
					fileChooser.getExtensionFilters().add(extFilter);

					//Show open file dialog
					File inputFile = fileChooser.showOpenDialog(null);

					if(inputFile == null) return;

					try {
						FileInputStream inputStream = new FileInputStream(inputFile);

						String everything = IOUtils.toString(inputStream);
						txaSVContent.setText(everything);
						ExtDescription ed = tblExternalDescription.getSelectionModel().getSelectedItem();

						ed.setSvContent(everything);
						updateExternalDescription();

						inputStream.close();	
						saved = false;

					} catch (FileNotFoundException e) {
						Dialogs.showErrorDialog(null, e.toString(), "Error", "JPSearch Editor");
					} catch (IOException e) {
						Dialogs.showErrorDialog(null, e.toString(), "Error", "JPSearch Editor");	
					} catch (Exception e) {						
						Dialogs.showErrorDialog(null, e.toString(), "Error", "JPSearch Editor");
					}
				}
				else{
					Dialogs.showWarningDialog(null, "Select an External Description first." , "Warning", "JPSearch Editor");
				}
			}
		});

		btnPersonAffAdd.setOnAction(new EventHandler() {			 
			@Override
			public void handle(Event arg0) { 
				if(tblROIPerson.getSelectionModel().getSelectedItem() != null){
					List<String> names = new ArrayList<String>();
					names.add("Name");
					CDOrganizationType newOrgType = new CDOrganizationType(names, 
							new Place("", new ArrayList<String>(),new ArrayList<String>()));

					CDPerson p = tblROIPerson.getSelectionModel().getSelectedItem();
					p.getAffiliation().add(newOrgType);//have to add to the original Object as well

					tblROIPersonAffiliation.getItems().add(newOrgType);
					tblROIPersonAffiliation.getSelectionModel().selectLast();
					saved = false;
				}
				else{
					Dialogs.showErrorDialog(null, "No Person was selected" , "Error", "JPSearch Editor");
				}
			}
		});
		btnPersonAffDelete.setOnAction(new EventHandler() {			 
			@Override
			public void handle(Event arg0) { 
				if(tblROIPersonAffiliation.getSelectionModel().getSelectedItem() != null){		
					CDOrganizationType cdo = tblROIPersonAffiliation.getSelectionModel().getSelectedItem();
					CDPerson p = tblROIPerson.getSelectionModel().getSelectedItem();
					p.getAffiliation().remove(cdo);//have to add to the original Object as well

					int index = tblROIPersonAffiliation.getSelectionModel().getSelectedIndex();
					if(index >= 0){
						if(Dialogs.showConfirmDialog(null, "Are you sure you want to delete this Affiliation?", "Confirmation", "JPSearch Editor", DialogOptions.YES_NO) == DialogResponse.YES){
							tblROIPersonAffiliation.getItems().remove(index);
							tblROIPersonAffiliation.getSelectionModel().selectNext();
							updateCDPerson();
							if(tblROIPersonAffiliation.getItems().size() == 0){
								txaPersonAffiliationAddressName.setText("");
								txaPersonAffiliationAddressDesc.setText("");
							}
							saved = false;
						}
					}
					else{
						Dialogs.showWarningDialog(null, "No items exist" , "Warning", "JPSearch Editor");
					}
				}
				else{
					Dialogs.showErrorDialog(null, "No Affiliation was selected" , "Error", "JPSearch Editor");
				}
			}
		});

		btnPersonAdd.setOnAction(new EventHandler() {			 
			@Override
			public void handle(Event arg0) { 
				if(tblRegionOfInterest.getSelectionModel().getSelectedItem() != null){
					CDPerson p = new CDPerson(new Person(
							"[GivenName]",
							"[FamilyName]"), 
							new ArrayList<CDOrganizationType>(),
							new Place(),
							"",
							""); 

					RegionOfInterest roi = tblRegionOfInterest.getSelectionModel().getSelectedItem();
					roi.getContentDescription().getPerson().add(p);

					tblROIPerson.getItems().add(p);
					tblROIPerson.getSelectionModel().selectLast();
					saved = false;
				}
				else{
					Dialogs.showErrorDialog(null, "None Region Of Interest was selected" , "Error", "JPSearch Editor");
				}
			}
		});
		btnPersonDelete.setOnAction(new EventHandler() {			 
			@Override
			public void handle(Event arg0) { 
				if(tblROIPerson.getSelectionModel().getSelectedItem() != null){

					int index = tblROIPerson.getSelectionModel().getSelectedIndex();
					if(index >= 0){
						if(Dialogs.showConfirmDialog(null, "Are you sure you want to delete this Person?", "Confirmation", "JPSearch Editor", DialogOptions.YES_NO) == DialogResponse.YES){

							RegionOfInterest roi = tblRegionOfInterest.getSelectionModel().getSelectedItem();
							CDPerson p = tblROIPerson.getSelectionModel().getSelectedItem();
							roi.getContentDescription().getPerson().remove(p);

							tblROIPerson.getItems().remove(index);
							tblROIPerson.getSelectionModel().selectNext();
							if(tblROIPerson.getItems().size() == 0){
								tblROIPersonAffiliation.getItems().clear();
							}
							saved = false;
						}
					}
					else{
						Dialogs.showWarningDialog(null, "No items exist in table" , "Warning", "JPSearch Editor");
					}
				}
				else{
					Dialogs.showErrorDialog(null, "None Person was selected" , "Error", "JPSearch Editor");
				}
			}
		});

		btnObjectAdd.setOnAction(new EventHandler() {			 
			@Override
			public void handle(Event arg0) { 
				if(tblRegionOfInterest.getSelectionModel().getSelectedItem() != null){
					List<String> name = new ArrayList<String>();
					name.add("Name");
					CDObject newObj = new CDObject(name, "", "");
					RegionOfInterest roi = tblRegionOfInterest.getSelectionModel().getSelectedItem();

					roi.getContentDescription().getObject().add(newObj); //have to add to the original Object as well

					tblROIObject.getItems().add(newObj);
					tblROIObject.getSelectionModel().selectLast();
					saved = false;
				}
				else{
					Dialogs.showErrorDialog(null, "None Region Of Interest was selected" , "Error", "JPSearch Editor");
				}
			}
		});
		btnObjectDelete.setOnAction(new EventHandler() {			 
			@Override
			public void handle(Event arg0) { 
				if(tblROIObject.getSelectionModel().getSelectedItem() != null){
					RegionOfInterest roi = tblRegionOfInterest.getSelectionModel().getSelectedItem();
					CDObject selObj = tblROIObject.getSelectionModel().getSelectedItem();				

					roi.getContentDescription().getObject().remove(selObj);
					int index = tblROIObject.getSelectionModel().getSelectedIndex();
					if(index >= 0){
						if(Dialogs.showConfirmDialog(null, "Are you sure you want to delete this Region of Interest?", "Confirmation", "JPSearch Editor", DialogOptions.YES_NO) == DialogResponse.YES){

							tblROIObject.getItems().remove(index);
							tblROIObject.getSelectionModel().selectNext();
							saved = false;
						}
					}
					else{
						Dialogs.showWarningDialog(null, "No items exist in table" , "Warning", "JPSearch Editor");
					}
				}	
				else{
					Dialogs.showErrorDialog(null, "None Object was selected" , "Error", "JPSearch Editor");
				}
			}
		});

		btnPlaceAdd.setOnAction(new EventHandler() {			 
			@Override
			public void handle(Event arg0) { 
				if(tblRegionOfInterest.getSelectionModel().getSelectedItem() != null){
					List<String> name = new ArrayList<String>();
					name.add("Name");
					List<String> desc = new ArrayList<String>();
					desc.add("Description");
					Place newPlace = new Place("", name, desc);
					RegionOfInterest roi = tblRegionOfInterest.getSelectionModel().getSelectedItem();

					roi.getContentDescription().getPlace().add(newPlace); //have to add to the original Object as well

					tblROIPlace.getItems().add(newPlace);
					tblROIPlace.getSelectionModel().selectLast();
					saved = false;
				}
				else{
					Dialogs.showErrorDialog(null, "None Region Of Interest was selected" , "Error", "JPSearch Editor");
				}
			}
		});
		btnPlaceDel.setOnAction(new EventHandler() {			 
			@Override
			public void handle(Event arg0) { 
				if(tblROIPlace.getSelectionModel().getSelectedItem() != null){
					RegionOfInterest roi = tblRegionOfInterest.getSelectionModel().getSelectedItem();
					Place selPl = tblROIPlace.getSelectionModel().getSelectedItem();				
					int index = tblROIPlace.getSelectionModel().getSelectedIndex();
					if(index >= 0){
						if(Dialogs.showConfirmDialog(null, "Are you sure you want to delete this Region of Interest?", "Confirmation", "JPSearch Editor", DialogOptions.YES_NO) == DialogResponse.YES){
							roi.getContentDescription().getPlace().remove(selPl); 
							tblROIPlace.getItems().remove(index);	
							saved = false;
						}
					}
					else{
						Dialogs.showWarningDialog(null, "No items selected" , "Warning", "JPSearch Editor");
					}
				}	
				else{
					Dialogs.showErrorDialog(null, "None Place was selected" , "Error", "JPSearch Editor");
				}
			}
		});

		btnEventAdd.setOnAction(new EventHandler() {			 
			@Override
			public void handle(Event arg0) { 
				if(tblRegionOfInterest.getSelectionModel().getSelectedItem() != null){
					List<String> label = new ArrayList<String>();
					label.add("Label");
					List<String> desc = new ArrayList<String>();
					desc.add("Description");
					CDEvent newEvent = new CDEvent(label, desc);
					RegionOfInterest roi = tblRegionOfInterest.getSelectionModel().getSelectedItem();

					roi.getContentDescription().getEvent().add(newEvent); //have to add to the original Object as well

					tblROIEvent.getItems().add(newEvent);
					tblROIEvent.getSelectionModel().selectLast();
					saved = false;
				}
				else{
					Dialogs.showErrorDialog(null, "None Region Of Interest was selected" , "Error", "JPSearch Editor");
				}
			}
		});
		btnEventDel.setOnAction(new EventHandler() {			 
			@Override
			public void handle(Event arg0) { 
				if(tblROIEvent.getSelectionModel().getSelectedItem() != null){
					RegionOfInterest roi = tblRegionOfInterest.getSelectionModel().getSelectedItem();
					CDEvent selEv = tblROIEvent.getSelectionModel().getSelectedItem();				
					int index = tblROIEvent.getSelectionModel().getSelectedIndex();
					if(index >= 0){
						if(Dialogs.showConfirmDialog(null, "Are you sure you want to delete this Region of Interest?", "Confirmation", "JPSearch Editor", DialogOptions.YES_NO) == DialogResponse.YES){
							roi.getContentDescription().getEvent().remove(selEv);					
							tblROIEvent.getItems().remove(index);
							tblROIEvent.getSelectionModel().selectNext();
							saved = false;
						}
					}
					else{
						Dialogs.showWarningDialog(null, "No items in table" , "Warning", "JPSearch Editor");
					}
				}
				else{
					Dialogs.showErrorDialog(null, "None Event was selected" , "Error", "JPSearch Editor");
				}			
			}
		});

		txaPersonAffiliationAddressName.focusedProperty().addListener(new ChangeListener<Boolean>() {
			@Override
			public void changed(ObservableValue<? extends Boolean> arg0, Boolean arg1, Boolean arg2) {
				// Handling only when focus is out.
				if(!arg2 && tblROIPersonAffiliation.getSelectionModel().getSelectedItem() !=null ){
					List<String> addNames = (tblROIPersonAffiliation.getSelectionModel()
							.getSelectedItem())
							.getAddress()
							.getName();
					addNames.clear();
					String c = txaPersonAffiliationAddressName.getText();
					for(String n : c.split("\n")){
						addNames.add(n);
					}

					updateCDPerson();
					saved = false;
				}
			}
		});

		txaPersonAffiliationAddressDesc.focusedProperty().addListener(new ChangeListener<Boolean>() {
			@Override
			public void changed(ObservableValue<? extends Boolean> arg0, Boolean arg1, Boolean arg2) {
				// Handling only when focus is out.
				if(!arg2 && tblROIPersonAffiliation.getSelectionModel().getSelectedItem() !=null ){
					List<String> addDescr = (tblROIPersonAffiliation.getSelectionModel()
							.getSelectedItem())
							.getAddress()
							.getDescription();
					addDescr.clear();
					String c = txaPersonAffiliationAddressDesc.getText();
					for(String n : c.split("\n")){
						addDescr.add(n);
					}

					updateCDPerson();
					saved = false;
				}
			}
		});

		btnROIDelete.setOnAction(new EventHandler() {
			@Override
			public void handle(Event arg0) {
				if(jpsC != null){
					RegionOfInterest selROI = tblRegionOfInterest.getSelectionModel().getSelectedItem();				
					jpsC.getRegionOfInterest().remove(selROI); //update original object

					int index = tblRegionOfInterest.getSelectionModel().getSelectedIndex();
					if(index >= 0){
						if(Dialogs.showConfirmDialog(null, "Are you sure you want to delete this Region of Interest?", "Confirmation", "JPSearch Editor", DialogOptions.YES_NO) == DialogResponse.YES){
							tblRegionOfInterest.getItems().remove(index);			
							tblRegionOfInterest.getSelectionModel().selectNext();

							//if no more items left, clear the sections
							if(tblRegionOfInterest.getItems().isEmpty()){
								tblROIPerson.getItems().clear();
								tblROIObject.getItems().clear();
								tblROIPlace.getItems().clear();
								tblROIEvent.getItems().clear();
								tblExternalDescription.getItems().clear();
								lstRegionLocator.getItems().clear();
								lstROIKeywords.getItems().clear();
							}
							saved = false;
						}
					}
					else{
						Dialogs.showErrorDialog(null, "No items exist in table" , "Error", "JPSearch Editor");
					}
				}
			}
		});

		lstROIKeywords.setOnKeyPressed(new EventHandler<KeyEvent>() {
			public void handle(KeyEvent ke) {		        
				if(ke.getCode() == KeyCode.DELETE){
					if(tblRegionOfInterest.getSelectionModel().getSelectedItem() != null){
						if(Dialogs.showConfirmDialog(null, "Delete this keyword?","Confirmation","JPSearch Editor", DialogOptions.YES_NO) == DialogResponse.YES){
							int selected = lstROIKeywords.getSelectionModel().getSelectedIndex();
							lstROIKeywords.getItems().remove(selected);
							tblRegionOfInterest.getSelectionModel().getSelectedItem().getKeywords().remove(selected);
							lstROIKeywords.getSelectionModel().clearSelection();
							saved = false;
						}
					}
					else{
						Dialogs.showWarningDialog(null, "None Region of Interest is selected","Warning","JPSearch Editor");
					}
				}
			}
		});

		lstRegionLocator.setOnKeyPressed(new EventHandler<KeyEvent>() {
			public void handle(KeyEvent ke) {		        
				if(ke.getCode() == KeyCode.DELETE){
					if(tblRegionOfInterest.getSelectionModel().getSelectedItem() != null &&
							lstRegionLocator.getSelectionModel().getSelectedItem() != null){
						if(Dialogs.showConfirmDialog(null, "Delete this region?","Confirmation","JPSearch Editor", DialogOptions.YES_NO) == DialogResponse.YES){
							int selected = lstRegionLocator.getSelectionModel().getSelectedIndex();
							lstRegionLocator.getItems().remove(selected);
							tblRegionOfInterest.getSelectionModel().getSelectedItem().getRegionLocator().getRegions().remove(selected);
							lstRegionLocator.getSelectionModel().clearSelection();
							setSnapshot(tblRegionOfInterest.getSelectionModel().getSelectedItem());
							saved = false;
						}
					}
					else{
						Dialogs.showWarningDialog(null, "None Region of Interest is selected","Warning","JPSearch Editor");
					}
				}
			}
		});

		imgScreenshot.setOnMouseClicked(new EventHandler<MouseEvent>() {
			@Override
			public void handle(MouseEvent mouseEvent) {
				if(mouseEvent.getButton().equals(MouseButton.PRIMARY)){
					if(mouseEvent.getClickCount() == 2){
						handleRegionTagging();
					}
				}
			}
		});
	}


	/**
	 * Set the Regions snapshot
	 * @param roiSel The region of interest that contains the regions
	 */

	public void setSnapshot(RegionOfInterest roiSel){
		Pane tempPane = new Pane();		
		ImageView img = new ImageView(imgThumbnail.getImage());	
		tempPane.getChildren().add(img);
		double ratio = imgThumbnail.getImage().getWidth() / imgThumbnail.getBoundsInParent().getWidth();
		int strokeSize = (int)(STROKE_SIZE * ratio);

		RegionLocator rl = roiSel.getRegionLocator();
		for(Region r : rl.getRegions()){

			//draw image tags
			int x = r.getArea().get(0);
			int y =  r.getArea().get(1);
			int toX = r.getArea().get(2);
			int toY = r.getArea().get(3);
			Rectangle rect = new Rectangle(x, y, toX-x, toY-y);
			rect.setStroke(Color.CHOCOLATE);
			rect.setStrokeWidth(strokeSize);
			rect.setFill(Color.TRANSPARENT);
			tempPane.getChildren().add(rect);	
			
		}

		imgScreenshot.setImage(tempPane.snapshot(new SnapshotParameters(), null));
	}

	/**
	 * Disables the interface components to false or true depending on the parameter
	 * @param disabled Value to be set
	 */
	private void setDisableInterface(boolean disabled){ 
		tabpaneAdvanced.setDisable(disabled);
		txtIdentifier.setDisable(disabled);
		txtDescription.setDisable(disabled);
		txtTitle.setDisable(disabled);
		txtPreferredValue.setDisable(disabled);
		txtCreationDate.setDisable(disabled);
		txtModifiedDate.setDisable(disabled);
		txtWidth.setDisable(disabled);
		txtHeight.setDisable(disabled);
	}

	/**
	 * Clear all interface's components
	 */
	private void clearAllFields(){		
		txtIdentifier.clear();
		txtWidth.clear();
		txtHeight.clear();
		txtDescription.clear();
		txtTitle.clear();
		txtPreferredValue.clear();
		txtCreationDate.clear();
		txtModifiedDate.clear();
		txtPubGName.clear();
		txtPubFName.clear();
		txtGPSlongitude.clear();
		txtGPSlatitude.clear();
		txtGPSaltitude.clear();
		txtRatingLblDefinition.clear();
		txtRatingLblValue.clear();
		txtOIIOriginationofID.clear();
		txtOIIIdentifer.clear();
		txtRDrdi.clear();
		txtRDdescription.clear();
		txtRDardr.clear();
		txtRDard.clear();	
		txaPubOrgName.clear();
		txaPubOrgAddName.clear();
		txaPubOrgAddDes.clear();
		lstKeywords.getItems().clear();
		lstCollectionLabel.getItems().clear();
		lstROIKeywords.getItems().clear();
		lstRegionLocator.getItems().clear();
		txaTNContent.clear();
		txaLVContent.clear();
		txaRVContent.clear();
		txaSVContent.clear();
		txaPersonAffiliationAddressName.clear();
		txaPersonAffiliationAddressDesc.clear();
		txtAddKeywordROI.clear();

		lblFileName.setText("");
		menuItemUpdate.setText("Update Metadata");

		InputStream is = this.getClass().getResourceAsStream("/empty.jpg");
		if(is != null){
			imgThumbnail.setImage(new Image(is));
			is = this.getClass().getResourceAsStream("/empty.jpg");
			imgScreenshot.setImage(new Image(is));
		}else{
			Pane tempPane = new Pane();

			Rectangle rect = new Rectangle(0, 0, imgThumbnail.getFitHeight(), imgThumbnail.getFitHeight());
			rect.setFill(Color.BLACK);
			tempPane.getChildren().add(rect);		
			imgThumbnail.setImage(tempPane.snapshot(new SnapshotParameters(), null));
			imgScreenshot.setImage(tempPane.snapshot(new SnapshotParameters(), null));
		}

		tblCreators.getItems().clear();
		tblModifiers.getItems().clear();
		tblPublishers.getItems().clear();
		tblRegionOfInterest.getItems().clear();
		tblExternalDescription.getItems().clear();
		tblROIPerson.getItems().clear();
		tblROIPersonAffiliation.getItems().clear();
		tblROIObject.getItems().clear();
		tblROIPlace.getItems().clear();
		tblROIEvent.getItems().clear();
		tblSources.getItems().clear();
		tblSourceElements.getItems().clear();

		saved = false;
	}

	/*
	 * Update Region of Interest methods
	 */
	public void updateExternalDescription(){
		RegionOfInterest roi = tblRegionOfInterest.getSelectionModel().getSelectedItem();
		if(roi != null){
			roi.getExternalDescription().clear();
			for(ExtDescription e : tblExternalDescription.getItems()){
				roi.getExternalDescription().add(new ExternalDescription(e));				
			}
		}
	}

	public void updateCDPerson(){
		RegionOfInterest roi = tblRegionOfInterest.getSelectionModel().getSelectedItem();
		if(roi != null){
			roi.getContentDescription().getPerson().clear();
			for(CDPerson p : tblROIPerson.getItems()){
				roi.getContentDescription().getPerson().add(p);				
			}
		}
	}
	//---------------------------------------

	/**
	 * Shows the metadata on the interface's components
	 */
	public void showMetadata(){

		//Basic type fields
		if(jpsC.getDescription() == null)
			txtDescription.setText("");
		else
			txtDescription.setText(jpsC.getDescription());

		if(jpsC.getIdentifier() == null)
			txtIdentifier.setText("");
		else
			txtIdentifier.setText(jpsC.getIdentifier());

		if(jpsC.getTitle() == null)
			txtTitle.setText("");
		else
			txtTitle.setText(jpsC.getTitle());

		txtCreationDate.setText(jpsC.getCreationDate().toString());
		txtModifiedDate.setText(jpsC.getModifiedDate().toString());		
		txtPreferredValue.setText(String.valueOf(jpsC.getPreferredValue()));

		//Creators - set values and editing
		ObservableList<Person> creators = FXCollections.observableArrayList(jpsC.getCreators());		
		crGivenName.setCellValueFactory(new PropertyValueFactory<Person,String>("GivenName"));
		crGivenName.prefWidthProperty().bind(tblCreators.widthProperty().divide(2));
		crGivenName.setEditable(true);		
		crFamilyName.setCellValueFactory(new PropertyValueFactory<Person,String>("FamilyName"));
		crFamilyName.prefWidthProperty().bind(tblCreators.widthProperty().divide(2));
		crFamilyName.setEditable(true);
		tblCreators.setItems(creators);

		//Modifiers
		ObservableList<Person> modifiers = FXCollections.observableArrayList(jpsC.getModifiers());		
		modGivenName.setCellValueFactory(new PropertyValueFactory<Person,String>("GivenName"));
		modGivenName.prefWidthProperty().bind(tblModifiers.widthProperty().divide(2));
		modGivenName.setEditable(true);		
		modFamilyName.setCellValueFactory(new PropertyValueFactory<Person,String>("FamilyName"));
		modFamilyName.setEditable(true);	
		modFamilyName.prefWidthProperty().bind(tblModifiers.widthProperty().divide(2));
		tblModifiers.setItems(modifiers);

		//Source
		ObservableList<Source> sources = FXCollections.observableArrayList(jpsC.getSource());	
		tblSources.setItems(sources);

		//Publisher
		if(!jpsC.getPublishers().isEmpty()){
			List<Publisher> pb = jpsC.getPublishers(); 
			ObservableList<Publisher> publishers = FXCollections.observableArrayList(pb);
			tblPublishers.setItems(publishers);
			String givenName = "";
			String familyName = "";
			String orgInfoName = "";
			String orgInfoAddrName = "";
			String orgInfoAddrDescr = "";

			txtPubGName.setText(givenName);
			txtPubFName.setText(familyName);			
			txaPubOrgName.setText(orgInfoName);
			txaPubOrgAddName.setText(orgInfoAddrName);
			txaPubOrgAddDes.setText(orgInfoAddrDescr);						
		}		

		//Rating
		Rating rating = jpsC.getRating();
		if(rating != null){
			txtRatingLblDefinition.setText(rating.getLabelDefinition());
			txtRatingLblValue.setText(rating.getLabelValue());
		}		

		//Original Image Identifier
		OriginalImageIdentifier oii = jpsC.getOriginalImageIdentifier(); 
		if(oii != null){
			txtOIIOriginationofID.setText(oii.getOriginationofID());
			txtOIIIdentifer.setText(oii.getIdentifier());
		}

		//Rights Description
		RightsDescription rd = jpsC.getRightsDescription();
		if(rd != null){
			txtRDard.setText(rd.getActualRightsDescription());
			txtRDardr.setText(rd.getActualRightsDescriptionReference());
			txtRDdescription.setText(rd.getDescription());
			txtRDrdi.setText(rd.getRightsDescriptionInformation());
		}

		//GPS Positioning
		GPSPositioning gpsp = jpsC.getGpsPositioning();
		if(gpsp != null){
			txtGPSlongitude.setText(String.valueOf(gpsp.getLongitude()));
			txtGPSlatitude.setText(String.valueOf(gpsp.getLatitude()));
			txtGPSaltitude.setText(String.valueOf(gpsp.getAltitude()));
		}

		//Keywords
		ObservableList<String> keywords = FXCollections.observableArrayList(jpsC.getKeywords());
		lstKeywords.setItems(keywords);

		//Collection Label
		ObservableList<String> collectionLabel = FXCollections.observableArrayList(jpsC.getCollectionLabel());
		lstCollectionLabel.setItems(collectionLabel);

		//RegionOfInterest
		ObservableList<RegionOfInterest> roi = FXCollections.observableArrayList(jpsC.getRegionOfInterest());	
		tblRegionOfInterest.setItems(roi);

		readWidthHeight();					
		imgThumbnail.setImage(actualImage);

		menuItemUpdate.setDisable(false);
		menuShowXML.setDisable(false);
		menuItemSaveXMLToFile.setDisable(false);
		menuItemOpenXMLFile.setDisable(false);
		menuItemSaveToDB.setDisable(false);
	}

	public void readWidthHeight(){
		if(jpsC.getWidth() == 0){
			txtWidth.setText(String.valueOf((int)actualImage.getWidth()));
		}
		else{
			txtWidth.setText(String.valueOf(jpsC.getWidth()));
		}
		if(jpsC.getHeight() == 0){
			txtHeight.setText(String.valueOf((int)actualImage.getHeight()));
		}
		else{
			txtHeight.setText(String.valueOf(jpsC.getHeight()));
		}
	}

	public void prepareIdentifier(){
		jpsC.setIdentifier(txtIdentifier.getText());
	}

	public void prepareCreatorsandModifiers(){
		jpsC.getCreators().clear();
		for(Person p : (ObservableList<Person>)tblCreators.getItems()){
			if(p != null)
				jpsC.getCreators().add(p);
		}

		jpsC.getModifiers().clear();
		for(Person p : (ObservableList<Person>)tblModifiers.getItems()){
			if(p != null)
				jpsC.getModifiers().add(p);
		}
	}

	public void preparePublisher(){

		List<Publisher> pbs = tblPublishers.getItems();		

		jpsC.getPublishers().clear();
		jpsC.setPublishers(pbs);
	}

	public void prepareDescription(){
		jpsC.setDescription(txtDescription.getText());
	}

	public void prepareTitle(){
		jpsC.setTitle(txtTitle.getText());
	}	

	public void prepareDates(){
		jpsC.setCreationDate(txtCreationDate.getText());
		jpsC.setModifiedDate(txtModifiedDate.getText());
	}

	public void preparePreferredValue(){
		if(StringUtils.isBlank(txtPreferredValue.getText()))
			jpsC.setPreferredValue(-1);
		else{
			int pv = Integer.parseInt(txtPreferredValue.getText());		
			jpsC.setPreferredValue(pv);		
		}
	}

	public void prepareRating(){
		Rating r = new Rating(txtRatingLblDefinition.getText(), txtRatingLblValue.getText());
		jpsC.setRating(r);
	}

	public void prepareOrigImageIdentifier(){
		String origID = txtOIIOriginationofID.getText();
		String id = txtOIIIdentifer.getText();
		OriginalImageIdentifier oii = new OriginalImageIdentifier(origID, id);
		jpsC.setOriginalImageIdentifier(oii);
	}

	public void prepareRightDescription(){
		String ard = txtRDard.getText();
		String rdar = txtRDardr.getText();
		String rdDes = txtRDdescription.getText();
		String rdi = txtRDrdi.getText();
		RightsDescription rd = new RightsDescription(rdi, rdDes, rdar, ard);
		jpsC.setRightsDescription(rd);
	}

	public void prepareGPSPositioning(){
		GPSPositioning gpsPositioning;
		if(!StringUtils.isBlank(txtGPSlongitude.getText()) &&
				!StringUtils.isBlank(txtGPSlatitude.getText()) &&
				!StringUtils.isBlank(txtGPSaltitude.getText())){
			double longitude = Double.parseDouble(txtGPSlongitude.getText());			
			double latitude = Double.parseDouble(txtGPSlatitude.getText());
			double altitude = Double.parseDouble(txtGPSaltitude.getText());
			gpsPositioning = new GPSPositioning(longitude, latitude, altitude);
		}else{
			gpsPositioning = new GPSPositioning();	
		}
		jpsC.setGpsPositioning(gpsPositioning);	
	}

	public void prepareSource(){
		jpsC.getSource().clear();
		for(Source s : (ObservableList<Source>)tblSources.getItems()){
			if(s != null)
				jpsC.getSource().add(s);
		}
	}

	public void prepareRegionOfInterest(){
		jpsC.getRegionOfInterest().clear();
		for(RegionOfInterest r : (ObservableList<RegionOfInterest>)tblRegionOfInterest.getItems()){
			if(r != null){				
				jpsC.getRegionOfInterest().add(r);
			}
		}
	}

	public void prepareWidthHeight(){
		int w = Integer.parseInt(txtWidth.getText());
		int h = Integer.parseInt(txtHeight.getText());

		jpsC.setWidth(w);
		jpsC.setHeight(h);		
	}

	public boolean writeXML2File(){
		String newXML = JPSearchMetadataHandler.marshal(jpsC);		   		
		File temp = new File("temp.jpg");
		try {
			new JPSearchRewriter().updateJPSearchData(image.getJPSearchFile(), temp, StringEscapeUtils.unescapeXml(newXML)); //Had to unescape first but problem with unmarshaller
			String fileName = image.getJPSearchFile().getAbsolutePath();
			if(image.getJPSearchFile().delete())
				temp.renameTo(new File(fileName));

			currentXML = newXML;
			Dialogs.showInformationDialog(null, fileName + " was updated successfully!", "Information", "JPSearch Editor");

		} catch (ImageReadException e) {
			Dialogs.showErrorDialog(null, e.toString(), "Error", "JPSearch Editor");
			return false;
		} catch (ImageWriteException e) {
			Dialogs.showErrorDialog(null, e.toString(), "Error", "JPSearch Editor");
			return false;
		} catch (IOException e) {
			Dialogs.showErrorDialog(null, e.toString(), "Error", "JPSearch Editor");
			return false;
		}	
		return true;
	}

	/*
	 * MENU FUNCTIONS! 
	 * 	 
	 */

	/**
	 * Opens a FileChooser to let the user select an address book to load.
	 */
	@FXML
	private void handleOpen() {

		if(jpsC != null && !saved){
			if(Dialogs.showConfirmDialog(null, "You have some changes that have not been saved. " +
					"Are you sure you want to load a new photo?", "Confirmation", "JPSearch Editor", DialogOptions.YES_NO) == DialogResponse.NO)
				return;
		}

		FileChooser fileChooser = new FileChooser();

		//Set extension filter
		FileChooser.ExtensionFilter extFilter = new FileChooser.ExtensionFilter("JPG files (*.jpg)", "*.jpg");
		fileChooser.getExtensionFilters().add(extFilter);

		if(inputImageFile != null){
			File existDirectory = inputImageFile.getParentFile();
			fileChooser.setInitialDirectory(existDirectory);
		}

		//Show open file dialog
		inputImageFile = fileChooser.showOpenDialog(null);				

		if(inputImageFile != null){
			clearAllFields();
			lblFileName.setText(inputImageFile.getName());
			String s = inputImageFile.toURI().toString();
			actualImage = new Image(s);

			image = new JPSearchParser(inputImageFile.getAbsolutePath());
			if(image.getJpsearchData() == null){				
				Dialogs.showWarningDialog(null, inputImageFile.getName() + " does not contain JPSearch metadata! Any new metadata will be added to image"
						, "Warning", "JPSearch Editor");
				jpsC = new JPSearchCore();
				imgThumbnail.setImage(actualImage);
				readWidthHeight();
				menuItemUpdate.setText("Add JPS Metadata");
				menuItemUpdate.setDisable(false);
				menuItemSaveXMLToFile.setDisable(false);
				menuShowXML.setDisable(false);
				menuItemSaveToDB.setDisable(false);
				setDisableInterface(false);	
				menuItemOpenXMLFile.setDisable(false);
				saved = true;
				return;
			}

			//Image contains JPSearch metadata
			try{
				jpsC = JPSearchMetadataHandler.unmarshal(image.getXmlMetadata());
			}
			catch(Exception e){
				Dialogs.showErrorDialog(null, "Could not load image metadata. Maybe something went wrong from the " +
						" previous save?", "Error", "JPSearch Editor");
				setDisableInterface(true);
				return;
			}
			currentXML = image.getXmlMetadata();
			//clearAllFields();
			showMetadata();	

			Dialogs.showInformationDialog(null, inputImageFile.getName() + " was loaded successfully!", "Information", "JPSearch Editor");
			setDisableInterface(false);
			saved = true;
		}	
	}

	/**
	 * Updates Metadata of the file
	 */
	@FXML
	private void handleUpdate(){

		//Change identifier
		prepareIdentifier();

		//Change creators
		prepareCreatorsandModifiers();

		//Change publisher
		preparePublisher();

		//Change description
		prepareDescription();

		//Change Dates
		prepareDates();

		//Change Title
		prepareTitle();

		//Change preferred value
		preparePreferredValue();

		//Change Rating
		prepareRating();

		//Change OriginalImageIdentifier
		prepareOrigImageIdentifier();

		//Change Rights Description
		prepareRightDescription();

		//Change GPS Positioning
		prepareGPSPositioning();

		//Change Source
		prepareSource();

		//Change Region of Interest
		prepareRegionOfInterest();

		//Change Width & Height
		prepareWidthHeight();

		//Write new JPSearch Core to file
		writeXML2File();
		saved = true;

		menuItemUpdate.setText("Update Metadata");
	}

	/**
	 * Saves Metadata to the file
	 */
	@FXML
	private void handleSaveXMLToFile(){		

		FileChooser fileChooser = new FileChooser();

		//Set extension filter
		FileChooser.ExtensionFilter extFilter = new FileChooser.ExtensionFilter("XML files (*.xml)", "*.xml");
		fileChooser.getExtensionFilters().add(extFilter);

		//Show open file dialog
		File outputXML = fileChooser.showSaveDialog(null);
		if(outputXML == null) return;

		if(!outputXML.getAbsolutePath().contains(".xml")){
			outputXML = new File(outputXML.getAbsolutePath() + ".xml");
		}
		JPSearchMetadataHandler.marshal(jpsC, outputXML);
		Dialogs.showInformationDialog(null, "XML metadata was successfully saved to file: " + outputXML.getAbsolutePath(), "Information", "JPSearch Editor");
	}

	/**
	 * Saves Metadata to DB. At this time it is just a file that the XML metadata is appended
	 */
	@FXML
	private void handleSaveToDB(){

		BufferedWriter bw = null;

		try {			
			if(!dbFile.exists())
				dbFile.createNewFile();

			FileWriter fw = new FileWriter(dbFile.getAbsoluteFile(),true); //append to existing file
			bw = new BufferedWriter(fw);

			//write basic tag
			bw.write(inputImageFile.getName() + "\n");
			bw.write(JPSearchMetadataHandler.marshal(jpsC) + "\n");
			Dialogs.showInformationDialog(null, "XML metadata was successfully saved to database.", "Information", "JPSearch Editor" );
			System.out.println(dbFile.getAbsolutePath());

		}catch (IOException e) {
			Dialogs.showErrorDialog(null, e.toString(), "Error", "JPSearch Editor");
		}
		finally{
			try {
				bw.close();
			} catch (IOException e) {
				e.printStackTrace();
			}
		}
	}

	/**
	 * Closes the application.
	 */
	@FXML
	private void handleExit() {
		mainApp.exitApplication();
	}

	/**
	 * Shows about message.
	 */
	@FXML
	private void handleAbout() {
		Dialogs.showInformationDialog(null, "Programmer: Nicos Demetriou\nDone for UPC and professor Jaime Delgado");
	}

	/**
	 * Inserts an XML file with metadata
	 */
	@FXML
	private void handleOpenXML() {

		FileChooser fileChooserInstance = new FileChooser();
		//Set extension filter
		FileChooser.ExtensionFilter extFilter = new FileChooser.ExtensionFilter("XML files (*.xml)", "*.xml");
		fileChooserInstance.getExtensionFilters().add(extFilter);

		//set initial directory same as the previous
		if(inputImageFile != null){
			File existDirectory = inputImageFile.getParentFile();
			fileChooserInstance.setInitialDirectory(existDirectory);
		}

		fileChooserInstance.setTitle("Select XML Instance");
		File xmlInstance = fileChooserInstance.showOpenDialog(null);	

		if(xmlInstance == null)
			return;

		FileChooser fileChooserRule = new FileChooser();
		//Set extension filter
		extFilter = new FileChooser.ExtensionFilter("XML files (*.xml)", "*.xml");
		fileChooserRule.getExtensionFilters().add(extFilter);

		//set initial directory same as the previous
		File existDirectory = xmlInstance.getParentFile();
		fileChooserRule.setInitialDirectory(existDirectory);

		fileChooserRule.setTitle("Select Rule file for XML Instance");
		File xmlRule = fileChooserRule.showOpenDialog(null);

		//If user selected at least the XML instance file
		if(xmlInstance != null){

			if(xmlRule == null){ //in case Rule step is avoided this means that the user wants to load JPSearch XML from file
				jpsC = JPSearchMetadataHandler.unmarshal(xmlInstance);
				try{
					BufferedReader reader = new BufferedReader(new FileReader(xmlInstance));
					String line, results = "";
					while( ( line = reader.readLine() ) != null)
					{
						results += line + "\n";
					}
					reader.close();
					currentXML = results;
					clearAllFields();
					showMetadata();	
					Dialogs.showInformationDialog(null, "XML Instance was load successfully", "Information", "JPSearch Editor");				

				}
				catch (Exception e){
					currentXML = "Error occured";
					Dialogs.showErrorDialog(null, "Error occurred while reading the XML instance. Perhaps a non JPSearch format file?",
							"Error","JPSearch Editor");
				}				
			}

			/*
			 * Translation Part
			 */
			else{
				Map<String, String> unknownElements = new LinkedHashMap<String, String>();
				JPSearchCore temp = null;
				try{
					temp = JPSearchTranslator.translateToJPSCore(xmlInstance, xmlRule, unknownElements);
				}
				catch(Exception e){
					e.printStackTrace();
					Dialogs.showErrorDialog(null, "Error translating XML instance, maybe something wrong with rules",
							"Error", "JPSearch Editor");
					return;
				}
				
				jpsC = null;
				jpsC = temp;
				String updateMenuText = menuItemUpdate.getText(); //preserve update menu text
				clearAllFields();
				menuItemUpdate.setText(updateMenuText);

				if(unknownElements.isEmpty()){					
					Dialogs.showInformationDialog(null, "XML Instance was translated and loaded successfully", "Information", "JPSearch Editor");
					showMetadata();	
				}
				else{
					//Handle unmapped elements
					showMetadata();	

					String keysMissing = "";
					String valuesKeysMissing = "";
					Iterator<Map.Entry<String, String>> iterator = unknownElements.entrySet().iterator();
					
					if(unknownElements.size() < 10){
						while (iterator.hasNext()) {//iterate through tokens and create String
							Map.Entry<String, String> pair = (Entry<String, String>) iterator.next();						
							keysMissing += pair.getKey();
							valuesKeysMissing += pair.getValue() + " ";
							if(iterator.hasNext())
								keysMissing += ", ";
						}
					}
					else{
						while (iterator.hasNext()) {//iterate through tokens and create String
							Map.Entry<String, String> pair = (Entry<String, String>) iterator.next();
							valuesKeysMissing += pair.getValue() + " ";
						}
						
						keysMissing = unknownElements.keySet().iterator().next();
						keysMissing += " and " + (unknownElements.size()-1) + " more elements";
					}

					if(Dialogs.showConfirmDialog(null, "XML Instance was translated and loaded successfully! " +
							"The elements " + keysMissing + " couldn't be mapped. Do you want to add their values to the " +
							"Description field?", "Confirmation", "JPSearch Editor", DialogOptions.YES_NO) == DialogResponse.YES){
						txtDescription.setText(txtDescription.getText() + " " + valuesKeysMissing);
						jpsC.setDescription(txtDescription.getText());
					}

				}
				try {
					JPSearchTranslator.dumpRulesToFile(xmlRule, "outputFile.txt");
				} catch (JAXBException e) {
					e.printStackTrace();
				} catch (IOException e) {
					e.printStackTrace();
				}
				currentXML = JPSearchMetadataHandler.marshal(jpsC);
			}				
		}
	}

	/**
	 * Launches Rule Generator
	 */
	@FXML
	private void handleLaunchRuleGenerator() {
		mainApp.showRuleGenerator();
	}	

	/**
	 * Called when the user clicks the new button.
	 * Opens a dialog to edit details for a new person.
	 */
	@FXML
	private void handleRegionTagging() {
		if(tblRegionOfInterest.getSelectionModel().getSelectedItem() != null){
			int index = tblRegionOfInterest.getSelectionModel().getSelectedIndex();
			RegionOfInterest newRoi = tblRegionOfInterest.getSelectionModel().getSelectedItem();			
			boolean okClicked = mainApp.showRegionTagging(newRoi, imgThumbnail.getImage(), imgScreenshot);
			if (okClicked) {

				//Reload values to the region of interest tab
				List<RegionOfInterest> list = new ArrayList<RegionOfInterest>(tblRegionOfInterest.getItems());
				tblRegionOfInterest.getSelectionModel().clearSelection();
				tblRegionOfInterest.getItems().clear();

				for(RegionOfInterest r : list)
					tblRegionOfInterest.getItems().add(new RegionOfInterest(r));

				tblRegionOfInterest.getSelectionModel().select(index);

				ObservableList<String> keywords = FXCollections.observableArrayList(newRoi.getKeywords());
				lstROIKeywords.setItems(keywords);
				saved = false;
			}
		}
		else{
			Dialogs.showWarningDialog(null, "Please select a Region of Interest first", "Warning", "JPSearch Editor");
		}
	}

	/**
	 * Called when the user clicks the new button.
	 * Opens a dialog to edit details for a new person.
	 */
	@FXML
	private void handleNewRegionTagging() {
		if(jpsC != null){
			RegionOfInterest newRoi = new RegionOfInterest();
			boolean okClicked = mainApp.showRegionTagging(newRoi, imgThumbnail.getImage(), imgScreenshot);
			if (okClicked) {
				jpsC.getRegionOfInterest().add(newRoi); //update original object
				tblRegionOfInterest.getItems().add(newRoi);
				tblRegionOfInterest.getSelectionModel().selectLast();
				saved = false;
			}
		}
	}

	/**
	 * Called when the user clicks the add Keyword
	 * in Additional Info
	 */
	@FXML
	private void handleAddToKeywords(){
		if(txtAddKeyword.getText() != null && !StringUtils.isBlank(txtAddKeyword.getText())){			
			String keyword = txtAddKeyword.getText();
			jpsC.getKeywords().add(keyword);
			lstKeywords.getItems().add(keyword);
			txtAddKeyword.clear();
			saved = false;
		}
	}

	/**
	 * Called when the user clicks the add collection label
	 * in Additional Info
	 */
	@FXML
	private void handleAddToCollectionLabel(){
		if(txtAddCollectionLabel.getText() != null && !StringUtils.isBlank(txtAddCollectionLabel.getText())){			
			String collectionLabel = txtAddCollectionLabel.getText();
			jpsC.getCollectionLabel().add(collectionLabel);
			lstCollectionLabel.getItems().add(collectionLabel);
			txtAddCollectionLabel.clear();
			saved = false;
		}
	}

	/**
	 * Called when the user clicks the add Keyword
	 * in Region of Interest
	 */
	@FXML
	private void handleAddToRoiKeywords(){
		if(tblRegionOfInterest.getSelectionModel().getSelectedItem() != null){
			if(txtAddKeywordROI.getText() != null && !StringUtils.isBlank(txtAddKeywordROI.getText())){
				RegionOfInterest r = tblRegionOfInterest.getSelectionModel().getSelectedItem();
				String keyword = txtAddKeywordROI.getText();
				r.getKeywords().add(keyword);
				lstROIKeywords.getItems().add(keyword);
				txtAddKeywordROI.clear();
				saved = false;
			}
		}
		else{
			Dialogs.showWarningDialog(null, "Please select a Region of Interest first", "Warning", "JPSearch Editor");
		}
	}

	/**
	 * Show XML Metadata in new window
	 */
	@FXML
	private void handleShowXML(){
		currentXML = JPSearchMetadataHandler.marshal(jpsC, null);
		mainApp.showXmlMetadata(StringEscapeUtils.unescapeXml(currentXML));
	}

	/**
	 * Deletes a publisher
	 */
	@FXML
	private void handleDeletePublisher(){
		if(tblPublishers.getSelectionModel().getSelectedItem() != null){
			if(Dialogs.showConfirmDialog(null, "Do you wish to delete this Publisher?", "Confirmation", "JPSearch Editor", DialogOptions.YES_NO) == DialogResponse.YES){
				int index = tblPublishers.getSelectionModel().getSelectedIndex();
				tblPublishers.getItems().remove(index);
				saved = false;
			}
		}
		else{
			Dialogs.showWarningDialog(null, "Please select a publisher from the table first.", "Warning", "JPSearch Editor");
		}
	}

	/*
	 * Adds a new publisher
	 */
	@FXML
	private void handleAddPublisher(){
		String givenName = txtPubGName.getText();
		String familyName = txtPubFName.getText();
		String orgInfoName = txaPubOrgName.getText();		
		String orgInfoAddrName = txaPubOrgAddName.getText();
		String orgInfoAddrDescr = txaPubOrgAddDes.getText();

		//if all empty don't do anything
		if(StringUtils.isBlank(givenName) && StringUtils.isBlank(familyName) && StringUtils.isBlank(orgInfoName)
				&& StringUtils.isBlank(orgInfoAddrName) && StringUtils.isBlank(orgInfoAddrDescr))
			return;

		//if no item selected means new Publisher is to be added
		if(tblPublishers.getSelectionModel().getSelectedItem() == null){
			Place newPlace = new Place(orgInfoAddrName, orgInfoAddrDescr);		

			Person newPerson = new Person(givenName, familyName);
			CDOrganizationType newCDO = new CDOrganizationType(orgInfoName, newPlace);

			Publisher newPub = new Publisher(newPerson, newCDO);
			tblPublishers.getItems().add(newPub);

		}
		//Update a publisher
		else{
			Publisher currentPub = tblPublishers.getSelectionModel().getSelectedItem();
			currentPub.setPersonGivenName(givenName);
			currentPub.setPersonFamilyName(familyName);
			currentPub.setOrgInfoName(orgInfoName);
			currentPub.setOrgInfoAddressName(orgInfoAddrName);
			currentPub.setOrgInfoAddressDescription(orgInfoAddrDescr);

			List<Publisher> list = new ArrayList<Publisher>(tblPublishers.getItems());
			tblPublishers.getSelectionModel().clearSelection();
			tblPublishers.getItems().clear();

			for(Publisher p : list)
				tblPublishers.getItems().add(new Publisher(p));

			btnAddPublisher.setText("Add New Publisher");
		}

		tblPublishers.getSelectionModel().clearSelection();
		saved = false;
		handleClearPublisherFields();
	}

	/**
	 * Clears add new publisher fields
	 */
	@FXML
	private void handleClearPublisherFields(){
		txtPubGName.clear();
		txtPubFName.clear();
		txaPubOrgName.clear();		
		txaPubOrgAddName.clear();
		txaPubOrgAddDes.clear();
		btnAddPublisher.setText("Add New Publisher");
		tblPublishers.getSelectionModel().clearSelection();
	}	

	/**
	 * Open Date Editor
	 */
	@FXML
	private void handleOpenCreateDateEditor(){
		if(mainApp.showDateEditor(txtCreationDate))
			saved = false;
	}


	/**
	 * Open Date Editor
	 */
	@FXML
	private void handleOpenModifiedDateEditor(){
		if(mainApp.showDateEditor(txtModifiedDate))
			saved = false;
	}

}